24

GCC9 已经实现了std::is_constant_evaluated. 我用它玩了一点,我意识到它有点棘手。这是我的测试:

constexpr int Fn1()
{
  if constexpr (std::is_constant_evaluated())
    return 0;
  else
    return 1;
}

constexpr int Fn2()
{
  if (std::is_constant_evaluated())
    return 0;
  else
    return 1;
}

int main()
{
  constexpr int test1 = Fn1(); // Evaluates to 0
  int test2 = Fn1();           // Evaluates to 0
  int const test3 = Fn1();     // Evaluates to 0

  constexpr int test4 = Fn2(); // Evaluates to 0
  int test5 = Fn2();           // Evaluates to 1
  int const test6 = Fn2();     // Evaluates to 0
}

根据这些结果,我得出以下结论:

  • if constexpr (std::is_constant_evaluated())总是评估 true分支。因此,使用这种结构是没有意义的。

  • 如果编译器在编译时评估一个变量, std::is_constant_evaluated())则 is true,无论该变量是否被显式注释constexpr

我对吗?

4

2 回答 2

19

if constexpr需要一个条件的常量表达式。is_constant_evaluated在这种情况下,当然总是如此。

它适用于常规if. constexpr目的是在常量表达式中求值时不进入函数中非法的代码路径。但是让它在运行时执行。它并不是要从函数中完全消除这些代码路径。

于 2019-01-18T10:09:43.513 回答
16

这就是我的想法,也许你会发现这很有帮助……也许没有。请注意,我认为写作if constexpr (std::is_constant_evaluated())将是一个非常常见的错误,而且很容易掉入陷阱。但希望编译器能诊断出这种情况。已提交91428,已针对 gcc 10.1 修复。


我们基本上有两种不同的代码规则——正常运行时代码的典型规则,以及用于constexpr编程的常量表达式的限制。这些是 expr.const 限制:没有 UB、noreinterpret_cast等。这些限制从语言标准到语言标准不断减少,这很棒。

基本上,控制流(从代码路径的角度来看)在“完整运行时”模式和模式之间交替constexpr。一旦我们进入constexpr模式(无论是通过初始化一个constexpr对象还是评估一个模板参数或......),我们就会一直呆在那里直到我们完成......然后我们回到完整的运行时模式。

什么is_constant_evaluated()是简单的:我处于 constexpr 模式吗?它会告诉您是否处于需要常量表达式的上下文中。

在这种情况下,让我们看一下if constexpr (is_constant_evaluated())。无论我们曾经处于什么状态,都if constexpr需要一个常量表达式作为其初始化,所以如果我们还没有,这会将我们提升到 constexpr 模式。因此,is_constant_evaluated()它是正确的——无条件的。

但是,对于if (is_constant_evaluated()),一个简单的if并不会改变我们在运行时和 constexpr 之间的状态。所以这里的值取决于调用它的上下文。初始化test4使我们进入 constexpr 模式,因为它是一个 constexpr 对象。在其初始化期间,我们遵循常量表达式规则......is_constant_evaluated()确实如此。但是一旦我们完成了,我们又回到了运行时规则......所以在初始化test5,is_constant_evaluated()是错误的。(然后test6是一个不幸的语言特例——你可以使用常量整数变量作为常量表达式,因此我们以同样的方式对待它们的初始化。)

于 2019-01-18T15:27:13.240 回答