当前草案中[expr.ref]/(4.2)与[expr.sub]/1是否存在矛盾?
咱们来仔细拆解这个问题——你疑惑的是C++标准草案里[expr.ref]/(4.2)和[expr.sub]/1这两条规则,在处理A().a[0]这类表达式时会不会产生冲突,对吧?其实这两条规则是自洽的,咱们一步步理清楚:
首先看**[expr.ref]/(4.2)**的核心规则:
若E2是非静态数据成员,E1的类型为“cq1 vq1 X”,E2的类型为“cq2 vq2 T”,则该表达式指代E1所指对象的指定成员。若E1是lvalue,则
E1.E2是lvalue;否则E1.E2是xvalue。vq12是vq1与vq2的“并集”(只要其中一个是volatile,vq12就是volatile,const同理),cv12是cq1与cq2的“并集”。
对于A().a来说,A()是prvalue(临时对象),显然不是lvalue,所以按照这条规则,A().a的结果是xvalue,类型对应成员a的类型(假设a是数组类型,那就是数组类型的xvalue)。
接下来看**[expr.sub]/1**的规则,它规定E1[E2]等价于*(E1 + E2)(除非E1是数组类型或重载了operator[],这里咱们讨论原生数组的情况)。那对A().a[0]来说:
- 首先
A().a是数组xvalue,数组到指针的转换会把它转换成指向数组首元素的prvalue指针(根据数组转换规则,数组类型的rvalue/xvalue可以转成对应指针类型的prvalue); - 然后
E1 + 0(也就是数组指针加0)得到的还是prvalue指针; - 最后解引用这个prvalue指针时,根据解引用规则:解引用指针的结果,若指针是prvalue(既不是lvalue也不是函数指针),则结果是xvalue。
所以绕一圈下来,A().a[0]最终被推导为xvalue,和[expr.ref]/(4.2)一路传递下来的值类别完全一致,两条规则并没有矛盾。
可能容易混淆的点在于:数组xvalue经过下标操作后,直觉上可能觉得应该是lvalue,但标准里的规则链是连贯的——从成员访问得到xvalue数组,到数组转prvalue指针,再到解引用prvalue指针得到xvalue,每一步都严格遵循标准定义,逻辑上完全自洽。
内容的提问来源于stack exchange,提问作者Belloc

