常量整数和常量求值

Constant integers and constant evaluation

考虑以下程序:

#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;
}

prints -1 when run(魔杖盒)。

但是,如果我在编译时计算函数 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;
}

compiles fine and outputs 1(魔杖盒)。为什么我没有得到编译失败?

不断评价是不是很有趣?

语言中有几个地方我们尝试进行持续评估,如果失败,我们回退到不做-不断的评估。静态初始化是一个这样的地方,初始化常量整数是另一个地方。

发生了什么:

int const i = f();

是这个可以不断求值,但不一定非得如此。因为(非constexpr)常量整数仍然可以用作常量表达式,如果满足所有其他条件,我们必须尝试。例如:

const int n = 42;       // const, not constexpr
std::array<int, n> arr; // n is a constant expression, this is ok

所以我们尝试这样做 - 我们将 f() 称为常量表达式。在这种情况下,std::is_constant_evaluated()true,因此我们使用 throw 命中分支并最终失败。不能 throw 在持续评估期间,因此我们的持续评估失败。

但随后我们回退,再次尝试 - 这次将 f() 作为非常量表达式调用(即 std::is_constant_evaluated()false)。这条路径成功,给我们 1,因此 i 被初始化为值 1。但值得注意的是,i 此时 不是 常量表达式。随后的 static_assert(i == 1) 将是错误格式的,因为 i 的初始值设定项 不是 常量表达式!即使非常量初始化路径恰好(否则)完全满足常量表达式的要求。


请注意,如果我们尝试:

constexpr int i = f();

这会失败,因为我们无法回退到非常量初始化。