[英]C++20 | std::is_constant_evaluated() and const variables
Let's consider the following code:让我们考虑以下代码:
#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;
}
It returns 0 with both gcc and clang .它返回 0 与gcc 和 clang 。 I would have expected it to return 1 instead.我原以为它会返回 1。
If foo()
is made constexpr
, while b
is kept simply const
, then it does return 1.如果foo()
是constexpr
,而b
只是保持const
,那么它会返回 1。
What am I missing here?我在这里错过了什么? Thanks!谢谢!
std::is_constant_evaluated()
returns true if and only if [meta.const.eval] :当且仅当[meta.const.eval]时, std::is_constant_evaluated()
返回 true:
evaluation of the call occurs within the evaluation of an expression or conversion that is manifestly constant-evaluated调用的评估发生在表达式或转换的评估中,该表达式或转换显然是常量评估的
This term, "manifestly constant-evaluated" (defined here ), refers to contexts that have to be constant-evaluated.这个术语“明显不断评估”(定义在这里)指的是必须不断评估的上下文。 A call to a non- constexpr
function (the nearest enclosing context here) is never constant evaluated, because it's non- constexpr
, so this is straight-forwardly not "manifestly constant-evaluated."对非constexpr
function(此处最近的封闭上下文)的调用永远不会进行常量评估,因为它是非constexpr
的,所以这显然不是“明显的常量评估”。
Once we make it constexpr
though, we're in this weird legacy quirk.但是,一旦我们将其constexpr
,我们就陷入了这个奇怪的遗留怪癖中。 Before C++11, which introduced constexpr
, we could still do stuff like this:在引入constexpr
的 C++11 之前,我们仍然可以这样做:
template <int I> void f();
const int i = 42; // const, not constexpr
f<i>(); // ok
Basically, we have this carve out for specifically integral (and enumeration) types declared const that are initialized with a constant expression.基本上,我们对声明为 const 的特定整数(和枚举)类型进行了划分,这些类型使用常量表达式进行了初始化。 Those still count as constant expressions.那些仍然算作常量表达式。
So this:所以这:
const auto b = foo(0);
If foo(0)
is an integral constant expression, then b
is something that could be used as a compile time constant (and would be constant-initialized † , if it were at namespace scope).如果foo(0)
是一个整数常量表达式,那么b
是可以用作编译时常量的东西(如果它在命名空间范围内,它将被常量初始化† )。 So what happens here is we do a two-step parse.所以这里发生的是我们进行两步解析。 We first try to evaluate foo(0)
as if it were a constant expression and then, if that fails, fall back to not doing that.我们首先尝试对foo(0)
求值,就好像它是一个常量表达式一样,然后,如果失败,则退回到不这样做。
In this first parse, with foo(0)
evaluated as a constant, is_constant_evaluated()
is (by definition) true
, so we get 1
.在第一个解析中,将foo(0)
评估为常量, is_constant_evaluated()
是(根据定义) true
,所以我们得到1
。 This parse succeeds, so we end up with b
as a compile-time constant.此解析成功,因此我们最终将b
作为编译时常量。
† For namespace-scope variables, constant-initialization is an important concept as well - to avoid the static initialization order fiasco. †对于命名空间范围的变量,常量初始化也是一个重要的概念——避免 static 初始化顺序的失败。 It leads to other gnarly examples (see P0595 ).它导致了其他粗糙的例子(见P0595 )。
The important thing here is basically: is_constant_evaluated()
should only be switched on to select a compile-time-safe algorithm vs a runtime algorithm, not to actually affect the semantics of the result.这里重要的事情基本上是: is_constant_evaluated()
应该只打开到 select 编译时安全算法与运行时算法,而不是实际影响结果的语义。
You have to be a little careful with where and how you use is_constant_evaluated
.您必须小心使用is_constant_evaluated
的位置和方式。 There are 3 kinds of functions in C++, and is_constant_evaluated
only makes sense in one of them. C++中有3种函数, 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
// ...
}
Another common mistake worth pointing out is that is_constant_evaluated
doesn't make any sense in an if constexpr
condition either:另一个值得指出的常见错误是is_constant_evaluated
在if constexpr
条件下也没有任何意义:
{
if constexpr (std::is_constant_evaluated()) // pointless: always true
// regardless of whether foo
// is run-time or compile-time
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.