我正在阅读 cppreference 的示例,网址为 https://en.cppreference.com/w/cpp/language/constant_expression
具体来说,这个:
constexpr int incr(int& n)
{
return ++n;
}
constexpr int g(int k)
{
constexpr int x = incr(k); // error: incr(k) is not a core constant
// expression because lifetime of k
// began outside the expression incr(k)
return x;
}
constexpr int h(int k)
{
int x = incr(k); // OK: x is not required to be initialized
// with a core constant expression
return x;
}
constexpr int y = h(1); // OK: initializes y with the value 2
// h(1) is a core constant expression because
// the lifetime of k begins inside the expression h(1)
所以,据我所知,这段代码的问题是:
constexpr int x = incr(k);
它违背了这个核心表达规则: “修改对象,除非该对象具有非易失性文字类型并且其生命周期从表达式的求值开始”。
但是为什么即使编译器在编译时以相同的方式对其进行评估,它仍然有效? (我们可以用
std::is_constant_evaluated()
来检查)
无论如何,编译器在编译时对其求值是否违反了“核心表达式规则”?
int x = incr(k); // k lifetime didn't start in this expression, so incr should not be able to mutate it since the "core constant expression" rules disallow it
这里的问题——尽管非常微妙——是
constexpr
使用错误对于 g()
或 h()
函数上的说明符来说是 not(所有 that 都表明对于它们来说这是 possible)进行编译时评估)。错误位于
constexpr int x = incr(k);
函数中的特定行 g()
(由于给出的原因)。
在
h()
函数中,类似的行没有 constexpr
说明符……但这并不意味着它不能在编译时进行评估。当调用 h(1)
时,整个函数可以(并且将会)在编译时进行评估,因此不存在 constexpr
冲突。
更改为使用
h(x)
调用,其中参数不是文字(或其他核心常量表达式),您将看到错误:
#include <iostream>
constexpr int incr(int& n)
{
return ++n;
}
constexpr int h(int k)
{
int x = incr(k); // OK: x is not required to be initialized
// with a core constant expression
return x;
}
int main()
{
constexpr int y = h(1); // OK - as explained on cppreference
int a = 42;
constexpr int z = h(a); // Illegal - "Variable 'a' cannot be used as a constant"
std::cout << y << " " << z << "\n"; // To prevent optimizing-away.
return 0;
}
来自您链接的同一个 cppreference 页面(粗体强调我的):
说明符声明可以评估 编译时函数或变量的值。这样的变量和 然后可以在只有编译时间常量的地方使用函数 允许使用表达式 (只要有适当的函数 给出了论据).constexpr
incr(k)
不是核心常量表达式,但它可以是核心常量表达式 E 的一部分,只要 E 足够大以包含变量 k
的定义。
当您执行
constexpr int x = incr(k);
时,您会强制编译器检查“从 int
初始化 incr(k)
”是否是常量表达式 ([dcl.constexpr]/6)。为了使其成为常量表达式,它必须是核心常量表达式 ([expr.const]/13)。它不是核心常量表达式,因为它修改了 k
,而其生命周期并不是在其中开始的。所以这个constexpr
初始化是不允许的。
这样做就好了
constexpr int y = h(1);
这里,“从
int
初始化 h(1)
”可以是一个常量表达式,因为 k
变量的生命周期会递增,仅在调用 h
时才开始。