引用初始化和常量表达式

Reference initialization and constant expressions

本文关键字:表达式 常量 初始化 引用      更新时间:2023-10-16

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

int main() {
const int& ri = 0;
constexpr int i = ri;
}

错误是关于ri的值在常量表达式中不可用。0当然是一个核心常量表达式,作为一个 prvalue 核心常量表达式似乎满足这些约束(很简单,因为int不是类、指针或数组类型)。那么ri不应该满足这个标准吗?

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

struct X { };
int main() {
const X& rx = X{};
constexpr X x = rx;
}

在此语句中:

const int& ri = 0;

0是一个 prvalue,但ri不是从该 prvalue 初始化的。prvalue 首先经历临时具体化转换,引用绑定到生成的 glvalue。由于ri绑定到这个物化的glvalue,而不是像你(I)怀疑的那样直接绑定到prvalue,因此相关的限制不是prvalue核心常量表达式限制(0确实满足),而是glvalue核心常量表达式限制 - 实体是常量表达式的允许结果。该限制的拼写清晰度略有提高,是:

静态存储持续时间为以下对象的任一对象:

  • 不是临时对象,或
  • 其值满足上述约束的临时对象,

或者它是一个函数。

我们的glvalue是一个临时对象,其值满足"上述约束"(这里的"以上"是指prvalue核心常量限制,int微不足道地满足),但它没有静态存储持续时间。

没有静态存储持续时间 →实体不是常量表达式的允许结果 → glvalue 表达式不是常量表达式 →ri未使用常量表达式初始化→ri不能在核心常量表达式中使用→i声明格式不正确。

同样的参数也适用于适当的类类型。

正如你所指出的,2.11 指出核心常量表达式的计算结果不得为:

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

    • 它使用常量表达式或

进一步的expr.const#6:

常量表达式是 glvalue 核心常量表达式 指作为常量允许结果的实体 表达式(定义如下)或 prvalue 核心常量表达式 其值满足以下约束:

实体是常量表达式的允许结果,如果它是 具有非临时静态存储持续时间的对象 对象或 是其值满足上述条件的临时对象 约束,或者它是一个函数。

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