作为社区 wiki 的较长评论。
表达方式xs[0]
在 [expr.sub]/1 中定义为*((xs)+(0))
。 (请参阅下面的括号。)
其中一个表达式的类型应为“指向T
” 另一个应具有无作用域枚举或整数类型。
因此,应用数组到指针的转换 [conv.array]:
“数组”类型的左值或右值N T
”或“未知边界的数组T
” 可以转换为“指向的指针”类型的纯右值T
”。结果是指向数组第一个元素的指针。
请注意,它可以对左值进行操作,结果是prvalue, 0
因为整数文字也是纯右值。加法在 [expr.add]/5 中定义。由于两者都是prvalues,不需要左值到右值的转换。
int arr[3];
constexpr int* p = arr; // allowed and compiles
现在关键的一步似乎是间接的*
[expr.unary.op]/1
一元*
运算符执行间接寻址:应用它的表达式应是指向对象类型的指针,或指向函数类型的指针,结果是引用表达式所指向的对象或函数的左值。
所以,结果是xs[0]
是一个左值,引用第一个元素xs
数组,并且类型为int const
.
注意: [expr.prim.general]/6
带括号的表达式是主表达式,其类型和值与所括表达式的类型和值相同。括号的存在不影响表达式是否为左值。
如果我们现在看看 [expr.const]/2 中的项目符号,它们不允许某些表达式和转换出现在常量表达式中,那么唯一可以应用的项目符号(据我所知)是左值到右值的转换:
[...]
但是唯一真正的左值到右值转换(4.1)(不是 4.2,它是数组到指针)出现在评估中xs[0]
是从引用第一个元素的结果左值的转换。
对于OP中的例子:
int const xs[]{1, 2, 3};
int as[xs[0]]; // error.
这个元素xs[0]
具有非易失性 const 整型,其初始化发生在常量表达式之前,并且已使用常量表达式进行初始化。
顺便说一下,在[expr.const]/2引用的段落中添加了“注释”已添加以澄清 https://stackoverflow.com/a/7436323/420683这是合法的:
constexpr char c = "hello"[0];
请注意,字符串文字也是左值。
如果有人(可以将其更改为)解释原因,那就太好了xs[0]
不允许出现在常量表达式中。