为什么与数组一起使用时const
和使用时有区别?constexpr
int const xs[]{1, 2, 3};
constexpr int ys[]{1, 2, 3};
int as[xs[0]]; // error.
int bs[ys[0]]; // fine.
我希望xs[0]
andys[0]
都是常量表达式,但只有后者被视为常量。
作为社区 wiki 的更长评论。
该表达式xs[0]
在 [expr.sub]/1 中定义为*((xs)+(0))
. (请参阅下面的括号。)
其中一个表达式应具有“指针
T
”类型,而另一个应具有无作用域枚举或整数类型。
因此,应用了数组到指针的转换 [conv.array]:
N T
“array of ”或“array of unknown bound of ”类型的左值或右值T
可以转换为“pointer toT
”类型的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 中的项目符号,它不允许某些表达式和转换出现在常量表达式中,唯一可以应用的项目符号(AFAIK)是左值到右值的转换:
左值到右值的转换(4.1),除非它应用于
整数或枚举类型的非易失性左值,它引用具有先前初始化的非易失性 const 对象,用常量表达式初始化 [注意:字符串文字 (2.14.5) 对应于此类对象的数组。——尾注],或
字面量类型的非易失性左值,指的是用 定义的非易失性对象
constexpr
,或指代此类对象的子对象,或[...]
但是,根据 (4.1)(不是 4.2,它是数组到指针)的唯一真正的左值到右值转换出现在 的评估中xs[0]
是从结果左值引用第一个元素的转换。
对于 OP 中的示例:
int const xs[]{1, 2, 3};
int as[xs[0]]; // error.
此元素xs[0]
具有非易失性 const 整数类型,其初始化在其出现的常量表达式之前,并且已使用常量表达式进行初始化。
顺便说一句,在 [expr.const]/2 的引用段落中添加的“注释”已添加以澄清这是合法的:
constexpr char c = "hello"[0];
请注意,字符串文字也是左值。
如果有人(可以将其更改为)解释为什么xs[0]
不允许出现在常量表达式中,那就太好了。
与关键字不同, C++11constexpr
用于启用在编译时评估表达式。const
constexpr int ys[]{1, 2, 3};
在编译时评估,所以没有错误
什么时候ys[0]
使用。
另外,请注意这里使用了 C++11 统一初始化{}
其他例子:
constexpr int multipletwo(int x)
{
return 2*x;
}
int num_array[multipletwo(3)]; //No error since C++11, num_array has 6 elements.