为什么在使用数组时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]
和ys[0]
都是常量表达式,但只有后者才会被视为。
答案 0 :(得分:5)
作为社区维基的更长时间的评论。
表达式xs[0]
在[expr.sub] / 1中定义为*((xs)+(0))
。 (见下面的parantheses。)
其中一个表达式的类型为“指向
T
的指针”,另一个表达式应具有无范围的枚举或整数类型。
因此,应用了数组到指针的转换[conv.array]:
“
N T
数组”或“T
未知范围数组”的左值或右值可以转换为“指向T
的指针”的prvalue。结果是指向数组的第一个元素的指针。
注意它可以在左值上运行,结果是 prvalue ,0
,因为整数文字也是prvalue。添加在[expr.add] / 5中定义。由于两者都是 prvalues ,因此不需要左值到右值的转换。
int arr[3];
constexpr int* p = arr; // allowed and compiles
现在关键的一步似乎是间接*
[expr.unary.op] / 1
一元
*
运算符执行间接:应用它的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是引用对象或函数的左值其中的表达点。
因此,xs[0]
的结果是左值,引用xs
数组的第一个元素,类型为int const
。
N.B。 [expr.prim.general] / 6
带括号的表达式是一个主表达式,其类型和值与所附表达式的类型和值相同。括号的存在不会影响表达式是否为左值。
如果我们现在查看[expr.const] / 2中不允许某些表达式和转换出现在常量表达式中的项目符号,可以应用的唯一项目符号(AFAIK)是左值到右值的转换:
左值 - 右值转换(4.1),除非它适用于
整数或枚举类型的非易失性glvalue,它引用具有前面初始化的非易失性const对象,用常量表达式初始化[注意:字符串文字(2.14.5)对应于数组这些物体。 - 尾注],或
文字类型的非易失性glvalue,它引用用
constexpr
定义的非易失性对象,或引用此类对象的子对象,或[...]
但是xs[0]
的评估中出现的唯一真正的左值(右)转换(不是4.2,即数组到指针)是从结果左值转换到第一个元素。
对于OP中的示例:
int const xs[]{1, 2, 3};
int as[xs[0]]; // error.
此元素xs[0]
具有非易失性const整数类型,其初始化位于其出现的常量表达式之前,并且已使用常量表达式初始化。
顺便说一下,在[expr.const] / 2 has been added to clarify的引用段落中添加的“注释”,这是合法的:
constexpr char c = "hello"[0];
请注意,字符串文字也是左值。
如果有人(可以改变这一点)解释为什么不允许xs[0]
出现在常量表达式中,那就太棒了。
答案 1 :(得分:0)
C ++ 11 constexpr
用于在编译时评估表达式,与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.