常数整数和常数求值

Pil*_*esa 38 c++ constexpr c++20

考虑以下程序:

#include <iostream>
#include <type_traits>

constexpr int f() {
  if (std::is_constant_evaluated())
    return -1;
  else return 1;
}

int main() {
  int const i = f();
  std::cout << i;
}
Run Code Online (Sandbox Code Playgroud)

打印-1在运行时(wandbox)。

但是,如果我throw在编译时求值时使用该函数:

#include <iostream>
#include <type_traits>

constexpr int f() {
  if (std::is_constant_evaluated())
    throw -1; // <----------------------- Changed line
  else return 1;
}

int main() {
  int const i = f();
  std::cout << i;
}
Run Code Online (Sandbox Code Playgroud)

它可以编译并输出1(wandbox)。为什么我没有出现编译失败?

Bar*_*rry 38

持续评估很有趣吗?

在该语言中,有一些地方我们尝试进行持续评估,如果失败,我们将退后一步进行非持续评估。静态初始化就是这样一个地方,初始化常量整数是另一个地方。

会发生什么:

int const i = f();
Run Code Online (Sandbox Code Playgroud)

是,这可能是恒定的评估,但它不一定是。因为(非constexpr)常量整数仍可以用作常量表达式,所以如果它们满足所有其他条件,我们必须尝试。例如:

const int n = 42;       // const, not constexpr
std::array<int, n> arr; // n is a constant expression, this is ok
Run Code Online (Sandbox Code Playgroud)

因此,我们尝试做-我们将其f()称为常量表达式。在这种情况下,std::is_constant_evaluated()is是true,所以我们用击打分支,throw最终失败。throw在持续评估期间无法执行,因此我们的持续评估失败。

但是,我们回退了,然后再试一次-这次f()以非常量表达式(即std::is_constant_evaluated()is false)的形式调用。该路径成功,给了我们1,所以i用value初始化1。但值得注意的是,i目前还不是一个常量表达式。static_assert(i == 1)因为i的初始值设定项不是常量表达式,所以后续项将格式错误!即使非常数初始化路径恰好(否则)完全满足常数表达式的要求。


请注意,如果我们尝试:

constexpr int i = f();
Run Code Online (Sandbox Code Playgroud)

这将失败,因为我们无法回退到非恒定初始化。

  • @GuillaumeRacicot:C ++ 03试图使用const来模仿它没有的constexpr。当然,我们现在应该使用真实的东西。 (7认同)
  • 为什么不应该对编译时间常数使用const的另一个原因。 (3认同)