让我们考虑以下代码:
#include <type_traits>
int foo(int arg) {
if (std::is_constant_evaluated()) {
return 1;
} else {
return 0;
}
}
int main() {
const auto b = foo(0);
return b;
}
使用 gcc 和 clang 都返回 0。我本来希望它返回 1。
如果
foo()
被制作为 constexpr
,而 b
被简单地保留为 const
,那么它确实返回 1。
我在这里缺少什么?谢谢!
std::is_constant_evaluated()
当且仅当 [meta.const.eval]: 时返回 true
调用的计算发生在显然是常量计算的表达式或转换的计算中
这个术语“明显不断评估”(定义见here),指的是必须不断评估的上下文。对非
constexpr
函数(这里最近的封闭上下文)的调用是never常量评估,因为它是非constexpr
,所以这直接不是“明显的常量评估”。
一旦我们成功了
constexpr
,我们就陷入了这个奇怪的遗留怪癖中。在引入 constexpr
的 C++11 之前,我们仍然可以做这样的事情:
template <int I> void f();
const int i = 42; // const, not constexpr
f<i>(); // ok
基本上,我们专门针对声明为 const 的整型(和枚举)类型进行了这种划分,并使用常量表达式进行初始化。这些仍然算作常量表达式。所以这个:
const auto b = foo(0);
如果
foo(0)
是一个整型常量表达式,那么 b
可以用作编译时常量(如果它在命名空间范围内,则将被常量初始化†
)。所以这里发生的是我们进行两步解析。我们首先尝试评估
foo(0)
,就好像它是一个常量表达式一样,然后,如果失败,则退回到 not这样做。在第一个解析中,将
foo(0)
计算为常量,is_constant_evaluated()
是(根据定义)
true
,因此我们得到
1
。解析成功,因此我们最终得到
b
作为编译时常量。†
P0595)。
这里重要的事情基本上是:is_constant_evaluated()
您必须小心使用地点和方式
is_constant_evaluated
仅在其中一种中有意义。
// a strictly run-time function
int foo(int arg)
{
if (std::is_constant_evaluated()) // pointless: always false
// ...
}
// a strictly compile time function
consteval int foo(int arg)
{
if (std::is_constant_evaluated()) // pointless: always true
// ...
}
// both run-time and compile-time
constexpr int foo(int arg)
{
if (std::is_constant_evaluated()) // ok: depends on context in
// which `foo` is evaluated
// ...
}
另一个值得指出的常见错误是 is_constant_evaluated
在 if constexpr
条件下也没有任何意义:
{
if constexpr (std::is_constant_evaluated()) // pointless: always true
// regardless of whether foo
// is run-time or compile-time
}