Call non constexpr from constexpr template function

与世无争的帅哥 提交于 2021-02-07 13:35:00

问题


I stumbled on constexpr template functions calling non constexpr functions: In the following snippet bar fails to compile as expected due to the call of non constexpr set but foo compiles. Can anyone tell me the reason why foo compiles?

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);
}

The compiler fails to compile with the error:

<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

Edit: Appended compiler error and rephrased question. Side effects are not in focus here.

As stated by bolov and Rekete1111 below the template will be evaluated later. When the constexpr restrictions are not met, the constexpr template function becomes some kind of semi constexpr function. The -Og compile result of the next code snippet shows, that the constexpr foo will be optimized out and the common foo2 not while both do not fulfill the requirements of constexpr functions (probably a consequence of the inline implication of 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);
}

The compile result:

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

回答1:


It's because foo is a function template and bar is a function.

For a function (e.g. bar) to be constexpr it must meet all of the constexpr rules (which change from standard to standard) and that is checked at the definition of the function. You get an error if those rules aren't met.

For a function template because you have only a template to generate functions you can't enforce the rules for constexpr. E.g. in your example at the point of the template definition you don't know if set<T>(x) is constexpr because you might have some template instantiations of set who are constexpr and some other template instantiations for set which are not. So you can't check that foo meets the requirements for constexpr. You can only check specific instantiations of foo if are constexpr e.g. foo<int> or foo<char> etc.

C++ handles this situation by allowing constexpr for a function template indiscriminately (sort of). However if a instantiations of the template doesn't meet the requirements for constexpr then that is allowed, but the specialization is not allowed in a a constant expression.

You can see this with a slightly modified code from your example:

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
}

§7.1.5 [dcl.constexpr]

  1. If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression. If no specialization of the template would satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed; no diagnostic required.


来源:https://stackoverflow.com/questions/48541044/call-non-constexpr-from-constexpr-template-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!