来自非 constexpr 调用的 constexpr 结果

constexpr result from non-constexpr call

最近令我惊讶的是以下代码也能在 clang、gcc 和 msvc 中编译(至少在它们当前的版本中)。

struct A {
    static const int value = 42;
};

constexpr int f(A a) { return a.value; }

void g() {
    A a;  // Intentionally non-constexpr.
    constexpr int kInt = f(a);
}

我的理解是对 f 的调用不是 constexpr,因为参数 i 不是,但看来我错了。这是正确的标准支持代码还是某种编译器扩展?

如评论中所述,常量表达式的规则通常不要求表达式中提到的每个变量,其生命周期开始于表达式评估之外的是 constexpr

a (long) list 的要求,当不满足时,表达式不能成为常量表达式。只要违反了none条,表达式就是常量表达式。

使用的 variable/object 是 constexpr 的要求正式称为对象 可用于常量表达式(尽管确切的定义包含更详细的要求和例外情况,另请参阅链接的 cppreference 页面)。

查看列表,您可以看到此 属性 仅在某些情况下才需要,即仅适用于 variables/objects,其生命周期开始于表达式之外,并且如果执行虚函数调用它,对其执行 lvalue-to-rvalue 转换,或者它是表达式中命名的引用变量。

这些情况均不适用于此处。没有涉及虚函数,a 不是引用变量。通常,lvalue-to-rvalue 转换会使需求变得重要。每当您尝试使用对象或其子对象之一中存储的值时,就会发生 lvalue-to-rvalue 转换。但是 A 是一个没有任何状态的空 class,因此没有可读的值。在将a传递给函数时,调用了隐式拷贝构造函数来构造f的参数,但由于class为空,实际上并没有做任何事情。它不访问 a.

的任何状态

请注意,如上所述,如果您使用引用,规则会更严格,例如

A a;
A& ar = a;
constexpr int kInt = f(ar);

会失败,因为 ar 命名了一个在常量表达式中不可用的引用变量。这有望很快得到修复,以更加一致。 (参见 https://github.com/cplusplus/papers/issues/973