const和constexpr数组之间的区别 [英] Difference between const and constexpr arrays

查看:115
本文介绍了const和constexpr数组之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么与数组一起使用时 const constexpr 有区别?

  int const xs [] {1,2,3}; 
constexpr int ys [] {1,2,3};

int as [xs [0]]; //错误。
int bs [ys [0]]; //罚款。

我会期望 xs [0] ys [0] 是常量表达式,但只有后者被视为这样。

解决方案

更长的评论作为社区wiki。






表达式 xs [0 ] 在[expr.sub] / 1中定义为 *((xs)+(0))


其中一个表达式的类型应为 T ,另一个应该有无范围的枚举或整数类型。


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


类型为 NT的数组的左值或右值 T 的未知边界数组转换为类型的指针 T 。结果是指向数组第一个元素的指针。


注意,它可以对左值操作,结果是 prvalue 0 作为整数文字也是prvalue。添加在[expr.add] / 5中定义。因为两者都是 ,所以不需要进行左值到右值的转换。

  int arr [3 ]。 
constexpr int * p = arr; //允许并编译

关键的步骤现在似乎是间接 * [expr.unary.op] / 1


一元 * 运算符执行间接:应用它的表达式应该是指向对象类型的指针,或者是指向函数类型的指针,结果是指向表达式指向的对象或函数的左值。 / p>

因此, xs [0] 的结果是一个左值, xs 数组的第一个元素,类型为 int const






注意[expr.prim.general] / 6


括号表达式是一个主要表达式,其类型和值与所括的表达式相同。括号的存在不影响表达式是否为左值。







现在看看[expr.const] / 2中不允许某些表达式和转换出现在常量表达式中的项目符号,唯一可以应用的项目符号(AFAIK)是左值到右值转换:



  • 一个左值到右值的转换(4.1),除非它应用到




    • 整数或枚举类型的非易失性glvalue,指的是具有前面初始化的非易失性const对象,用常量表达式初始化[注:字符串(2.14.5)对应于这样的对象的数组。 -end note]或


    • 一个非易失性字面值类型的glvalue,指的是用 constexpr ,或指向此类对象的子对象,或





[...]


但只有真正的左值到右值转换在 xs [0] 的评估中出现的(4.1)(不是4.2,它是数组到指针)是从引用第一个元素的结果左值的转换。



对于OP中的示例:

  int const xs [] {1,2,3}; 
int as [xs [0]]; //错误。

此元素 xs [0]






p>顺便提及,添加了[expr.const] / 2的引用段落中添加的注释以澄清< a>这是合法的:

  constexpr char c =hello[0] 

请注意,字符串文字也是一个左值。






如果有人(可以更改为)解释为什么不允许 xs [0] 出现在常量表达式中。


Why is there a difference between const and constexpr when used with arrays?

int const xs[]{1, 2, 3};
constexpr int ys[]{1, 2, 3};

int as[xs[0]];  // error.
int bs[ys[0]];  // fine.

I would expect both xs[0] and ys[0] to be constant expressions but only the latter is treated as such.

解决方案

A longer comment as community wiki.


The expression xs[0] is defined in [expr.sub]/1 as *((xs)+(0)). (See below for the parantheses.)

One of the expressions shall have the type "pointer to T" and the other shall have unscoped enumeration or integral type.

Therefore, the array-to-pointer conversion [conv.array] is applied:

An lvalue or rvalue of type "array of N T" or "array of unknown bound of T" can be converted to a prvalue of type "pointer to T". The result is a pointer to the first element of the array.

Note it can operate on an lvalue and the result is a prvalue, 0 as an integer literal is a prvalue as well. The addition is defined in [expr.add]/5. As both are prvalues, no lvalue-to-rvalue conversion is required.

int arr[3];
constexpr int* p = arr;  // allowed and compiles

The crucial step now seems to be the indirection * [expr.unary.op]/1

The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.

So, the result of xs[0] is an lvalue referring to the first element of the xs array, and is of type int const.


N.B. [expr.prim.general]/6

A parenthesized expression is a primary expression whose type and value are identical to those of the enclosed expression. The presence of parentheses does not affect whether the expression is an lvalue.


If we now look at the bullets in [expr.const]/2 which disallow certain expressions and conversions to appear in constant expressions, the only bullet that could apply (AFAIK) is the lvalue-to-rvalue conversion:

  • an lvalue-to-rvalue conversion (4.1) unless it is applied to

    • a non-volatile glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression [Note: a string literal (2.14.5) corresponds to an array of such objects. —end note ], or

    • a non-volatile glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an object, or

[...]

But the only true lvalue-to-rvalue conversion as per (4.1) (not 4.2, which is array-to-pointer) that appears in the evaluation of xs[0] is the conversion from the resulting lvalue referring to the first element.

For the example in the OP:

int const xs[]{1, 2, 3};
int as[xs[0]];  // error.

This element xs[0] has non-volatile const integral type, its initialization precedes the constant expression where it occurs, and it has been initialized with a constant expression.


By the way, the added "Note" in the quoted passage of [expr.const]/2 has been added to clarify that this is legal:

constexpr char c = "hello"[0];

Note that a string literal is an lvalue as well.


It would be great if someone (could change this to) explain why xs[0] is not allowed to appear in a constant expression.

这篇关于const和constexpr数组之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆