让我们先看看一些常见的左值和右值举例:
int var = 6; // var is a lvalue const int var = 6; // var is a (nonmodifiable) lvaluechar str[] = "hello, world"; // str is a (nonmodifiable) lvalue"hello, world"; // the expression is string literal, //so it's a rvaluestring("hello, world"); // the expresion is a (modifiable) rvalue
对于每一个表达式,其要么是一个左值(lvalues),要么是一个右值(rvalue)。
注1:对象类型为非函数、非引用、非void的类型;左值所对应的对象不一定实际存在。例如:char* p; *p这个左值所对应的对象目前是不存在的。 部分右值表达式也可以对应对象。例如,那些调用构造函数和调用那些返回类对象的函数的表达式,这些表达式可以调用相应对象的成员函数,但这些表达式是右值(此时,该右值可能是可修改的右值)。 |
右值的类型总是完整类型或者void类型。注意两点:(1)void类型(非完整类型)是右值;(2)当右值的类型不是void类型时,其必须是完整类型,因为需要计算它的值。由此可知,非void的非完整类型是左值。
注2:自定义运算符是函数,这类运算符是否需要或者产生左值取决于它们的参数和返回值类型。 |
注3:当左值类型T为非函数、非数组类型时,左值可以被转换为右值。如果T是非void的非完整类型,那么此时左值不应该被转化为右值来使用;如果该左值所对应的对象不是类型T的对象,也不是由T派生的类型的对象,或者该对象没有被初始化,那么发生这种转换的程序将产生未定义行为。 如果T是非类类型,那么转换得到的右值的类型是T的CV-qualified版本;否则,右值类型是T。 左值所对应的对象所包含的值就是右值的结果。 在发生这种转换时,左值的值并不是一定被计算。例如:当运算符sizeof的操作数发生这种转换时,并不需要访问左值的值,因为该运算符不需要计算它的操作数。 |
左值分为可修改的左值(modifiable)和不可修改的左值(nonmodifiable)。
如果要修改一个对象,该对象必须是左值;例外情况是,类类型的右值也可以在某些情形下修改它所对应的对象。例如,函数调用返回的类是右值,但可以调用其成员函数修改该对象。
由const限定的表达式不可以被修改;除非这是一个类类型,并且拥有mutable成员,那么该mutable成员可以被修改。
非类类型的右值总是由CV-qualifiers限定,类类型的右值可以没有CV-qualifiers。
部分内置运算符需要左值操作数。例如,所有内置赋值运算符要求它们的左操作数是一个左值(此时还是一个可修改的左值)。
部分内置运算符需要右值操作数,并且产生右值。例如,一元运算符和二元运算符“+”要求操作数为右值,并且它们产生的结果也为右值。
部分内置运算符和函数调用产生左值。例如,如果E是一个指针类型的表达式,那么*E是一个左值表达式(注:*运算符的操作数是左值,产生一个左值)。又如,函数 int& f();产生一个左值,因此f()的调用是一个左值表达式。
如果一个程序试图通过一个左值的非下列类型去访问存储在对象中的值,那么该行为是未定义的。
- 对象的动态类型
- 由CV-qulifiers限定的对象的动态类型
- 对象的动态类型的有符号和无符号类型
- 由CV-qulifiers限定的对象的动态类型的有符号和无符号类型
- 包含上述类型作为成员的聚合(aggregate)类型或者联合体类型。
- 对象的动态类型的基类的类型(可能被CV-qulifiers限定)
- char类型和unsigned char类型