简体   繁体   English

C++20 | std::is_constant_evaluated() 和 const 变量

[英]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_evaluatedif 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM