从constexpr模板函数调用non constexpr

Ale*_* R. 6 c++ gcc templates constexpr c++17

我偶然在调用非constexpr功能constexpr模板功能:在下面的代码片段无法编译预期由于非constexpr的通话设置,但FOO编译.谁能告诉我foo编译的原因?

template<class T>
void set(T& x){
    x++;
}

template<class T>
constexpr void foo(T& x){
    set<T>(x);
}

constexpr void bar(int& x){
    set<int>(x);
}

void bar(){
    int x = 5;
    foo(x);
    bar(x);
}
Run Code Online (Sandbox Code Playgroud)

编译器无法编译错误:

<source>: In function 'constexpr void bar(int&)':
<source>:12:13: error: call to non-constexpr function 'void set(T&) [with T = int]'
     set<int>(x);
     ~~~~~~~~^~~
Compiler returned: 1
Run Code Online (Sandbox Code Playgroud)

编辑:附加的编译器错误和重新定义的问题.这里没有副作用.

如下面的bolov和Rekete1111所述,模板将在稍后进行评估.当不满足constexpr限制时,constexpr模板函数成为某种半constexpr函数.该-oG编译下面的代码片段显示结果,该constexpr FOO将得到优化并共同foo2的不同时俩都没有履行constexpr功能(可能是一个的后果要求直列 constexpr的含义):

template<class T>
void set(volatile T& x){
    x++;
}

template<class T>
constexpr void foo(T& x){
    set<T>(x);
}

template<class T>
void foo2(T& x){
    set<T>(x);
}

void bar(){
    int x = 5;
    foo(x);
    foo2(x);
}
Run Code Online (Sandbox Code Playgroud)

编译结果:

void set<int>(int volatile&):
  ldr r3, [r0]
  add r3, r3, #1
  str r3, [r0]
  bx lr
void foo2<int>(int&):
  push {r4, lr}
  bl void set<int>(int volatile&)
  pop {r4, lr}
  bx lr
bar():
  push {r4, lr}
  sub sp, sp, #8
  add r4, sp, #8
  mov r3, #5
  str r3, [r4, #-4]!
  mov r0, r4
  bl void set<int>(int volatile&)
  mov r0, r4
  bl void foo2<int>(int&)
  add sp, sp, #8
  pop {r4, lr}
  bx lr
Run Code Online (Sandbox Code Playgroud)

bol*_*lov 7

这是因为它foo是一个功能模板而且bar是一个功能.

对于函数(例如bar)是constexpr,它必须满足所有constexpr规则(从标准变为标准)并且在函数的定义中进行检查.如果不满足这些规则,则会出错.

对于函数模板,因为您只有一个模板来生成函数,所以您无法强制执行constexpr的规则.例如,在模板定义点的例子中,你不知道,如果set<T>(x)constexpr因为你可能有一些模板实例set谁是constexpr和其他一些模板实例set哪些不是.所以你无法检查是否foo符合要求constexpr.您只能检查特定实例的foo,如果是constexprfoo<int>foo<char>等.

C++通过允许constexpr不加选择地(某种程度上)函数模板来处理这种情况.但是,如果模板的实例化不符合constexpr的要求则允许,但是在常量表达式中不允许使用特化.

您可以使用示例中略微修改的代码来查看此内容:

auto set(int a) { return a; }
constexpr auto set(char a) { return a; }

template<class T>
constexpr auto foo(T x){
    return set(x);
}

auto test()
{
    auto x = foo(24); // foo<int>  OK, no error
    //constexpr auto cx = foo(24) // foo<int> compiler error

    auto y = foo('a'); // foo<char> OK, no erro
    constexpr auto y = foo('a'); // foo<char> OK
}
Run Code Online (Sandbox Code Playgroud)

§7.1.5[dcl.constexpr]

  1. 如果constexpr函数模板的实例化模板特化或类模板的成员函数无法满足constexpr函数或constexpr构造函数的要求,那么该特化仍然是constexpr函数或constexpr构造函数,即使调用这样的函数不能出现在常量表达式中.如果模板的特化没有满足constexpr函数或constexpr构造函数的要求时被视为非模板函数或构造函数,则模板格式不正确; 无需诊断.