【问题标题】:Call non constexpr from constexpr template function从 constexpr 模板函数调用非 constexpr
【发布时间】:2018-07-10 11:55:32
【问题描述】:

我偶然发现了调用非 constexpr 函数的 constexpr 模板函数: 在以下 sn-p bar 由于调用非 constexpr set 而无法按预期编译,但 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);
}

编译器编译失败,报错:

<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

编辑:附加编译器错误和改写的问题。这里不关注副作用。

正如下面的 bolov 和 Rekete1111 所述,模板将在稍后进行评估。当不满足 constexpr 限制时,constexpr 模板函数变成某种半 constexpr 函数。下一段代码 sn-p 的 -Og 编译结果显示,constexpr foo 将被优化掉,而普通 foo2 not 而两者都做 not 满足 constexpr 函数的要求(可能是 constexpr 的 inline 含义的结果):

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

编译结果:

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

【问题讨论】:

  • “foo 有效”和“bar 失败”是什么意思?它不编译吗?意外行为?
  • 由于编译器错误无法编译。我会相应地更新我的问题。
  • 模板、构造函数和函数对 constexpr 有不同的规则。在您的示例中, set(x) 不能使用。只能进行一些持续的评估

标签: c++ templates gcc constexpr c++17


【解决方案1】:

因为foo是函数模板,bar是函数。

对于要成为 constexpr 的函数(例如 bar),它必须满足所有 constexpr 规则(从标准更改为标准),并且在函数定义时进行检查。如果不满足这些规则,您会收到错误消息。

对于函数模板,因为您只有一个模板来生成函数,所以您不能强制执行 constexpr 的规则。例如。在模板定义的示例中,您不知道set&lt;T&gt;(x) 是否为constexpr,因为您可能有一些set 的模板实例化是constexpr,而set 的一些其他模板实例化则不是。所以你不能检查foo 是否满足constexpr 的要求。如果是 constexpr,您只能检查 foo 的特定实例,例如foo&lt;int&gt;foo&lt;char&gt;

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
}

§7.1.5 [dcl.constexpr]

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

【讨论】:

  • +1 感谢“但是,这仅意味着如果规则允许,模板的实例化将是 constexpr,但如果不允许,则实例化将是非 constexpr 函数。”不知道。谢谢
  • 检查后,我认为 bolov 和 Rakete1111 的答案都有意义: constexpr 模板函数将在稍后进行评估。当模板调用不符合要求时,该函数将成为某种半 constexpr 函数。例如。使用优化 -Og 将优化 constexpr 函数。没有 constexpr 就不行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多