引用初始化和常量表达式

Bar*_*rry 7 c++ language-lawyer c++17

作为这个问题的后续,gcc和clang都认为这个程序格式不正确:

int main() {
    const int& ri = 0;
    constexpr int i = ri;
}
Run Code Online (Sandbox Code Playgroud)

该错误是关于ri在常量表达式中不可用的值.0肯定是一个核心常量表达式,并且作为一个prvalue核心常量表达似乎满足这些约束(int通常,因为不是类,指针或数组类型).所以不应该ri满足这个标准

如果我使用类类型的prvalue文字,情况也是如此:

struct X { };
int main() {
    const X& rx = X{};
    constexpr X x = rx;
}
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 5

在这个声明中:

const int& ri = 0;
Run Code Online (Sandbox Code Playgroud)

0是一个prvalue,但ri没有从该prvalue初始化.prvalue首先进行临时实现转换,并且引用绑定到生成的glvalue.由于ri绑定到这个物化的glvalue,而不是像你(I)所怀疑的那样直接与prvalue绑定,相关的限制不是prvalue核心常量表达式限制(它0确实满足)而是glvalue核心常量表达式限制 - 实体是持续表达允许结果.这种限制,略微提高了清晰度,是:

具有静态存储持续时间的对象是:

  • 不是临时对象,或
  • 一个临时对象,其值满足上述约束条件,

或者它是一个功能.

我们glvalue 一个临时的对象,其值满足"上述约束"("以上",这里指的是prvalue核心不变的限制,这int平凡的满足),但它并没有具有静态存储持续时间.

没有静态存储持续时间→实体不是常量表达式的允许结果→glvalue表达式不是常量表达式→ ri没有用常量表达式初始化→ ri不能用于核心常量表达式→声明i是病态的.

同样的论点也适用于适当的类类型.


小智 1

正如您所指出的,2.11 规定核心常量表达式不得计算为:

  • 引用引用类型的变量或数据成员的 id 表达式,除非引用具有前面的初始化并且

    • 它用常量表达式初始化或

进一步的 expr.const#6:

常量表达式可以是泛左值核心常量表达式,它引用作为常量表达式(如下定义)允许的结果的实体,也可以是纯右值核心常量表达式,其值满足以下约束:

...

如果实体是具有静态存储持续时间的对象,并且不是临时对象或者是值满足上述约束的临时对象,或者是函数,则该实体是常量表达式的允许结果。

从我的阅读来看,这意味着(用 X 代替某种类型)的 RHSconst X& r =必须是具有静态存储持续时间的对象满足上述条件的临时对象。由于 anint不适合类类型、指针类型或类/数组类型的对象,因此它不符合条件。