C++ &引用;如果constexpr";在C++;17在非模板函数中不起作用

C++ &引用;如果constexpr";在C++;17在非模板函数中不起作用,c++,c++17,if-constexpr,C++,C++17,If Constexpr,我尝试使用C++17标准。我尝试使用C++17的一个特性if constexpr。我有个问题。。。请看下面的代码。这样编译时不会出错。在下面的代码中,我尝试使用if constexpr检查它是否是指针 #include <iostream> #include <type_traits> template <typename T> void print(T value) { if constexpr (std::is_pointer_v<declty

我尝试使用C++17标准。我尝试使用C++17
的一个特性if constexpr
。我有个问题。。。请看下面的代码。这样编译时不会出错。在下面的代码中,我尝试使用
if constexpr
检查它是否是指针

#include <iostream>
#include <type_traits>

template <typename T>
void print(T value)
{
  if constexpr (std::is_pointer_v<decltype(value)>)
    std::cout << "Ptr to " << *value << std::endl; // Ok
  else
    std::cout << "Ref to " << value << std::endl;
}

int main()
{
  auto n = 1000;
  print(n);
  print(&n);
}
我得到一个编译错误:

main.cpp:8:32:错误:一元“*”的类型参数无效(具有“int”)
标准::cout
我想知道为什么“
if constexpr
”只在模板函数中起作用,即使类型是由输入参数的
decltype
推导出来的

这是故意的

if constexpr
不会实例化未执行的分支(如果该分支位于模板内)。它不会仅仅将未被接受的分支视为标记汤,并避免对其进行解析或完全执行语义分析。双方仍将进行分析,由于
*value
对于
int
s是格式错误的,因此这是一个错误


如果constexpr
,就不能使用
来避免编译非模板代码。这只是为了避免实例化对特定专门化可能无效的模板代码

C++标准,第9.4.1条:

如果If语句的形式为If constexpr,则条件值应为bool(8.6)类型的上下文转换常量表达式;这种形式称为constexpr if语句。如果已转换 如果条件为false,则第一个子语句为丢弃语句,否则第二个子语句(如果存在)为丢弃语句在封闭模板实体的实例化过程中(第17条),如果条件在实例化后不依赖于值,则丢弃的子状态(如果有)不会实例化。

(强调矿山)


因此,如果
constexpr的子语句不在模板内,它仍然会被实例化,因此它至少必须编译。

在模板外,会完全检查丢弃的语句。if constexpr不是#if预处理指令的替代品

我想知道为什么
如果constexpr
只在模板函数中工作,即使类型是由decltype从输入参数推导出来的

问题是,它也可以在非模板中工作,只是不像您期望的那样

要使
if constexpr
像您所说的那样工作,您不仅需要一个模板,还需要包含的表达式依赖于模板参数

让我们一步一步地来理解为什么C++中会这样做,它的含义是什么? 让我们从简单开始。下面的代码是否编译

void func_a() {
    nonexistant();
}
template<typename T>
void func_b() {
    nonexistant();
}
我想我们都会同意它不会编译,我们正在尝试使用一个尚未声明的函数

让我们添加一层

下面的代码是否编译

void func_a() {
    nonexistant();
}
template<typename T>
void func_b() {
    nonexistant();
}
答案是没有使用ODR。我们可以保留未定义的
helper_1
a
,并且不会出现链接器错误

更好的是,如果constexpr
,编译器不会实例化
的废弃分支中的模板:

template<typename T>
void helper_2() {
    T::nonexistant();
}

void func_d() {
    if constexpr (false) {
        helper_2<int>();
    }
}
这就像一开始我们的模板函数一样。我们说过,即使模板没有实例化,代码也是无效的,因为无效表达式不依赖于
t
。我们还说过,
如果constexpr
是实例化过程中的一个分支
。错误发生在实例化之前。这段代码也不会编译

最后,这段代码也不会编译:

void func_e() {
    if constexpr (false) {
        nonexistant();
    }
}
即使未实例化constexpr
内容,也会发生错误,因为完成了名字查找步骤,并且错误发生在实例化过程之前。只是在这种情况下,没有实例化,但在这一点上并不重要


那么,如果constexpr
,那么
有什么用途呢?为什么它似乎只在模板中工作

问题是,它在模板中的工作方式没有什么不同。正如我们在
func_b_2
中看到的那样,错误仍然会发生

但是,这个案例将起作用:

template<typename T>
void helper_3() {
    if constexpr (false) {
        T::nonexistant();
    }
}

void func_f() {
    helper_3<int>();
}
模板
void helper_3(){
if constexpr(false){
T::不存在();
}
}
void func_f(){
helper_3();
}
表达式
int::nonexistant()
无效,但代码无法编译。这是因为
T::nonexistant()
是一个依赖于
T
的表达式,因此名称查找在第二阶段完成。名称查找的第二阶段在模板实例化期间完成。包含
T::nonexistant()
if constexpr
分支始终是被丢弃的部分,因此名称查找的第二阶段永远不会完成

好了<代码>如果constexpr
不是关于不编译代码的一部分。就像模板一样,它们被编译,任何可以进行名称查找的表达式都被完成<代码>如果constexpr
是关于控制实例化的,即使在非模板函数中也是如此。适用于模板的所有规则也适用于
if constexpr
的所有分支。两阶段名称查找仍然适用,并且允许程序员不实例化代码的某些部分,否则如果实例化,这些部分将无法编译


因此,如果一个代码不能在未实例化的模板中编译,那么它将不会在未实例化的constexpr的
分支中编译。

您使用的编译器是什么?错误消息是什么?g++7.3和ubuntu 16.04使用GCC 7.1、7.2、7.3编译;GCC 6.3联机链接失败:g++-std=c++17 main.cpp-o test main.cpp:在函数“int main()”:main.cpp:8:32:错误:一元“*”(have'int')std::的类型参数无效最初我可能误读了您的答案,所以删除了我以前的注释,但在模板中我没有看到
template<typename T>
void func_b_2() {
    if constexpr (false) {
        nonexistant();
    }
}
void func_e() {
    if constexpr (false) {
        nonexistant();
    }
}
template<typename T>
void helper_3() {
    if constexpr (false) {
        T::nonexistant();
    }
}

void func_f() {
    helper_3<int>();
}