10

为什么与数组一起使用时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]都是常量表达式,但只有后者被视为常量。

4

2 回答 2

5

作为社区 wiki 的更长评论。


该表达式xs[0]在 [expr.sub]/1 中定义为*((xs)+(0)). (请参阅下面的括号。)

其中一个表达式应具有“指针T”类型,而另一个应具有无作用域枚举或整数类型。

因此,应用了数组到指针的转换 [conv.array]:

N T“array of ”或“array of unknown bound of ”类型的左值或右值T可以转换为“pointer to 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 中的项目符号,它不允许某些表达式和转换出现在常量表达式中,唯一可以应用的项目符号(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]不允许出现在常量表达式中,那就太好了。

于 2013-08-09T12:25:28.863 回答
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.
于 2013-08-09T11:24:30.357 回答