表达式按照图一所示的分类学进行分类。
expression / \ glvalue rvalue / \ / \ lvalue xvalue prvalue
glvalue
分类是一个通过求值去决定一个对象,一个位域或者一个函数的身份的表达式(identity
)。prvalue
分类是一个通过求值去初始化一个对象或者位域,或者计算运算符的操作数的值的一个表达式,具体是那种根据出现的上下文去决定。xvalue
分类是一个glvalue
,但是这个代表一个对象或者位域等等其资源能够重复利用的程序实体(通常是因为快靠近它们的生命周期的结尾)。[Note: 有些有rvalue
引用参与的表达式会产生一个xvalues
类型的值,比如:调用一个函数,其返回值是右值类型或者被转换成右值类型。]lvalue
分类是由glvalue
分类中那些除去xvalue
分类的剩余分类。rvalue
分类是由prvalue
分类和xvalue
分类组成的。每个表达式属于并且只属于这个分类方法中的基础分类的一种:lvalue
,xvalue
和prvalue
。表达式的这个属性叫做表达式的值分类。[Note: 在本章中,我们将描述8.5
章节中的内置运算符求值之后的值的分类和运算符的操作数期望的值的分类。举个例子,内置的赋值运算符期望它的左操作数的值分类是一个lvalue
,它的右操作数的值类型是一个prvalue
,并且求值之后的产生的结果的值类型是lvalue
。用户自定义的重载操作符的各种值的分类由操作符重载函数的形参声明类型和返回值的声明类型来确定。]
[Note: 由于历史原因,左值(lvalue
)和右值(rvalue
)的叫法是因为它们一个出现在赋值运算符的左边,一个出现在赋值运算符的右边。(现在这个分类的方法现在已经不太合适了,不能描述所有的可能情况);glvalue
是通用意义上的左值,prvalues
是纯净的右值。xvalue
是声明周期即将结束(eXpiring
)的左值。尽管它们的名字里面都是带有值(value
),但是他们不是针对(value
)的分类,而是针对表达式的分类。]
如果一个表达式满足一下条件之一,那么该表达式就属于xvalue
分类:
rvalue reference
)。rvalue reference
)。xvalue
类型的对象的一个访问类非静态并且不是引用类型的数据成员的类成员访问表达式的分类是xvalue
。.*
类型的指向类成员的表达式,其中运算符.*
的左边操作数的值分类是xvalue
,第二个操作数是指向数据成员的指针。简单来说,这条规则的效果是命名的右值(rvalue
)引用被当成一个左值(lvalue
),未命名的对象右值引用被当着xvalue
;函数的右值(rvalue
)引用,不管是否被命名都被当成xvalue
。
例子说明:
class A
{
int m;
};
A&& operator+(A, A);
A&& f();
A a;
A&& ar = static_cast<A &&>(a);
在这个例子中表达式f()
,f().m
,static_cast<A &&>(a)
和a + a
的分类是xvalue
。
一个prvalue
表达式的结果(result
)是表达式在上下文中存入的值。一个prvalue
分类的表达式的结果V
,有时候被叫做有值V
或者命名了值V
。pvalue
分类的表达式的结果对象(result object
)是一个被pvalue
分类表达式初始化的对象。一个被用来计算运算符的操作的值的pvalue
分类的表达式或者计算具有cv void
类型的值时没有结果对象(result object
)。[Note: 一个prvalue
分类表达式除了用在decltype-specifier
里面,一个类类型或者数组类型的pvalue
永远具有结果对象。对于丢弃的prvalue
表达式分类,一个临时对象被具体化了(todo: 这个是什么意思啊);]表达glvalue
分类的结果是该表达式标识的程序对象实体。
当一个glvalue
分类的表达式出现在一个要求操作数的分类是prvalue
的表达式中的时候,会对这个传入的glvalue
分类的表达式的值进行,lvalue-to-rvalue
,array-to-pointer
或者function-to-pointer
的标准转换,将一个glvalue
分类的值转换成prvalue
分类的值。[Note:将一个rvalue
分类的值绑定到一个lvalue
分类的值上,不在这个转换上下文考虑之中。][Note:当一个表达式的值是一个非类类型的时候,表达式的值的cv-qualifiers
在转换成prvalue
之前就被删除掉了。举个例子来说,一个值类型是const int
的lvalue
表达式可以被值类型为int
的prvalue
分类的表达式所接受。][Note: prvalue
分类的值类型不能为位域,当一个位域转换成一个prvalue
分类的值类型时,prvalue
分类的类型的位域创建之后马上就被做了整数提升转换。]
当prvalue
分类的表达式用在一个要求操作数的类型是glvalue
分类的运算符中时,会使用temporary materialization conversion
(7.4
)将prvalue
分类类型的表达式的值转换成xvalue
分类类型的表达式的值。
在11.6.3
中的引用初始化和15.2
中的临时对象相关的指出了lvalue
和rvalue
在其他重要的上下文中的的行为。
除非特别指出,一个prvalue
类型的表达式应该是完整类型或者void
类型。一个glvalue
分类的表达式不能是cv void
类型。[Note: glvalue
分类的表达式的类型可以是完整类型也可以是不完整的类型。prvalue
分类的表达式的值类型如果是类类型或者数组类型,那么可以是cv-qualified
类型,其他类型的pvalue
分类的表达式的值类型必须是cv-unqualified
类型。]
一个lvalue
分类的表达式的值是可修改的(modifiable)除非值类型是const-qualified
类型或者是函数类型。[Note:如果程序通过一个不可修改的左值表达式或者右值表达式去修改一个对象,那么程序是不符合规范的。]
如果一个程序通过一个glvalue
表达式去访问一个对象里面存储的值,这个对象的类型不是下面类型中的一个时,程序的行为是未定义的:
cv-qualified
版本。type similar
(在7.5
中定义)。signed
或者unsigned
类型。cv-qualified
版本类型对应的signed
或者unsigned
类型。cv-qualified
)。char
,unsigned char
,或者polar::byte
。如果一个表达式的类型是类型T
的引用类型,在进行其他分析之前表达式的类型会被调整成类型T
。表达式指定对象或者函数的引用并且表达式的分类是lvalue
或者xvalue
由表达式本身决定。[Note: 引用类型开始之前或者结束之后进行使用,程序的行为是未定义的。]
如果一个prvalue
分类的表达式初始类型是cv T
并且类型T
是一个cv-unqualified
的非类类型,非数组类型。表达式的类型在进行其他处理之前,会被调整为T
。
类型T1
和类型T2
的cv-combined
类型T3 similar to T1
并且cv-qualification
原型是:
i > 0
,cvi3是cvi1和cvi2的联合。const
将添加到所有的cvk3上面,其中0 < k < i
。[ Note: Given similar types T1 and T2, this construction ensures that both can be converted to T3. —end
note ]类型T1
的操作数p1
和类型T2
的操作数p2
的composite pointer type
,至少有一个是指针类型或者成员指针类型或者polar::nullptr_t
:
p1
跟p2
都是空指针常量的话组合结果是polar::nullptr_t
;p1
或者p2
是空指针常量,组合结果相应的是类型T2
或者类型T1
;T1
或者类型T2
,其中一个是指向cv1 void
的指针类型并且另一个是指向cv2 T
的指针类型,这里的T
是对象类型或者void
类型。那么他们组合指针类型是pointer to cv12 void
,这里的cv12
是cv1
和cv2
的联合。T1
或者类型T2
,其中一个是指向noexcept
函数类型的指针类型并且另一个是指向函数类型的指针类型,那么组合类型是指向函数类型的指针类型。T1
是指向cv1 C1
的指针并且T2
是指向cv2 C2
的指针,这里C1
是C2
的引用相关的类型,或者C2
是C1
的引用相关的类型。那么组合指针类型是T1
和T2
的cv-combined
类型或者是T2
和T1
的cv-combined
类型。T1
是指向类C1
的类型为cv1 U1
的成员的成员指针类型并且T2
是指向类C2
的类型为cv2 U2
的成员的成员指针类型,这里的C1
是C2
引用相关的类型或者C2
是C1
引用相关的类型。组合指针类型是T2
和T1
的cv-combined
类型,或者T1
和T2
的cv-combined
类型。T1
跟T2
是similar
类型,那么组合指针类型是T1
和T2
的cv-combined
类型。例子说明:
typedef void *p;
typedef const int *q;
typedef int **pi;
typedef const int **pci;
指针类型p
和指针类型q
的组合指针类型是const void *
,指针类型pi
和指针类型pci
的组合指针类型是const int * const *
。
discarded-value expression
),标准准换array-to-pointer
和function-to-pointer
不会被应用。当且仅当表达式的类型是被volatile-qualified
修饰glvalue
的分类的值类型并且是下面的情况之一时,表达式的值类型会被进行标准的lvalue-to-rvalue
转换:
(expression)
,这里的expression
列出的几种情况之一的表达式。id-expression
)。subscripting
)。class member access
)。indirection
)。pointer-to-member
)。conditional expression
),其中其二个和第三个操作数是这个列表中出现的表达式类型之一。comma expression
),其中右边的表达式是这里支持的表达式类型之一。
[Note: 使用重载的运算符会导致一个函数调用;上面的情况只包含内置的操作运算符的情况。]如果表达式(或者转换后的)的分类是prvalue
,那么temporary materialization conversion
将被应用。[Note: 如果表达式的类型是类类型的左值分类,那么它必须具有一个易变的构造函数去初始化由应用lvalue-to-rvalue
转换获取的临时对象。]glvalue
分类的表达式将会被求值,但是结果被丢弃。此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。