c++ constexpr一般混淆

6yoyoihd  于 2023-04-01  发布在  其他
关注(0)|答案(2)|浏览(150)

我昨天发了一个帖子,但是我觉得它不清楚,我得到的回复根本没有解决我的困惑。所以,我会试着让这个例子更简单。
为什么允许这样做:

constexpr int incr(int k1)
{
    return k1 + 5;
}

constexpr int foo(int k)  // runs in compile time
{
    return incr(k);
}

int main() {
    constexpr int x = 5;
    constexpr int y = foo(4);
}

但这个“非常”相似的功能不是吗?

constexpr int incr(int k1)
{
    return k1 + 5;
}

constexpr int foo(int k)  // runs in compile time
{
    constexpr int x = incr(k); // k: not a constant expression
    return x;
}

int main() {
    constexpr int x = 5;
    constexpr int y = foo(4);
}

在常量求值的上下文中,这两个函数究竟有什么不同?对我来说,它们看起来几乎完全相同,除了一个立即返回,而另一个首先计算值并将其赋值给constexpr变量。

如果可以的话,也请参考标准,以便我进一步阅读!

lrpiutwd

lrpiutwd1#

关于constexpr变量的规则是[dcl.constexpr]/6,
[...]在任何constexpr变量声明中,初始化的完整表达式应该是常量表达式([expr.const])。作为对象的constexpr变量,以及constexpr引用绑定到的任何临时变量,应该具有常量析构。
每个常量表达式都必须是一个核心常量表达式,还有一些额外的限制,在这里不相关,所以我不讨论它们([expr.const]/13)。如果constexpr变量的初始化不是一个核心常量表达式,那么这个程序就是病态的。
第二个例子违反的特定规则是[expr.const]/5.9。
表达式 E 是一个核心常量表达式,除非 E 的求值,遵循抽象机器的规则([intro.execution]),将求值以下之一:

  • [...]
  • 左值到右值的转换,除非将其应用于
  • 非易失性glvalue,它引用可用于常量表达式的对象,或者
  • 文字类型的非易失性glvalue,其引用其生存期在 E 的求值内开始的非易失性对象;

在宣言中

constexpr int x = incr(k);

完整表达式“从incr(k)初始化int变量”不能作为核心常量表达式,因为当它被求值时,它需要对k执行左值到右值转换,以便获得初始化k1所用的值。变量k在常量表达式中不可用,它的生存期也不是在全表达式“从incr(k)初始化int变量”内开始的。
在第一个例子中,你的编译器接受,incr(k) * 也 * 在编译时被求值,但它不需要是核心常量表达式,所以没有问题。一开始会很混乱,但我们需要记住,不是核心常量表达式的东西可以作为“更大”求值的一部分进行求值,“更大”求值是 * 核心常量表达式。在这种情况下,它是封闭的constexpr变量初始化(y的),它是核心常量表达式,因为它创建了函数参数k(用值4初始化它)然后 * 然后 * 从中读取。换句话说,如果 E 是“用foo(4)初始化y”,则k的生存期在 E 内开始,因此k的读取可以在不阻止 E 成为核心常量表达式的情况下发生。

pvabu6sv

pvabu6sv2#

constexpr修饰符应用于函数时,仅给出函数在编译时运行的可能性。(此外,在C++23草案中,constexpr函数不再受到以前的限制,因此您最好将其视为作者为支持constexpr语义而给出的契约“promise”)。修饰符并没有取消这一事实,同样的函数也可以在运行时运行:

int main() {
    constexpr int compileVar = foo(2);
    int param = 4;
    int runtimeVar = foo(param);
}

因此,不能保证传递给constexpr函数的参数在编译时计算。
另一方面,constexpr修饰符,当应用于 variables 时,只能**在编译时计算,编译器合理地阻止您使用函数的(可能)运行时参数来计算编译时变量。
在不断评估的背景下,这两种功能究竟有何不同?
我认为这里的主要误解是,如果constexpr函数是在编译时计算的,那么该函数的所有部分都是在编译时计算的,无论内部的变量是否有constexpr修饰符。因此,该函数的正确等效形式是:

constexpr int foo(int k)
{
    return incr(k);
}

将是:

constexpr int foo(int k)
{
    int x = incr(k);
    return x;
}

相关问题