An object is a region of storage that can be examined and stored into. An lvalue is an expression that refers to such an object. An lvalue does not necessarily permit modification of the object it designates. For example, a const object is an lvalue that cannot be modified. The termmodifiable lvalue is used to emphasize that the lvalue allows the designated object to be changed as well as examined. The following object types are lvalues, but not modifiable lvalues:
Because these lvalues are not modifiable, they cannot appear on the left side of an assignment statement.
The term rvalue refers to a data value that is stored at some address in memory. An rvalue is an expression that cannot have a value assigned to it. Both a literal constant and a variable can serve as an rvalue. When an lvalue appears in a context that requires an rvalue, the lvalue is implicitly converted to an rvalue. The reverse, however, is not true: an rvalue cannot be converted to an lvalue. Rvalues always have complete types or the void type.
ISO C defines a function designator as an expression that has function type A function designator is distinct from an object type or an lvalue. It can be the name of a function or the result of dereferencing a function pointer. The C language also differentiates between its treatment of a function pointer and an object pointer.
On the other hand, in C++, a function call that returns a reference is an lvalue. Otherwise, a function call is an rvalue expression. In C++, every expression produces an lvalue, an rvalue, or no value.
In both C and C++, certain operators require lvalues for some of their operands. The table below lists these operators and additional constraints on their usage.
Operator | Requirement |
---|---|
& (unary) | Operand must be an lvalue. |
++ -- | Operand must be an lvalue. This applies to both prefix and postfix forms. |
= += -= *= %= <<= >>= &= ^= |= | Left operand must be an lvalue. |
For example, all assignment operators evaluate their right operand and assign that value to their left operand. The left operand must be a modifiable lvalue or a reference to a modifiable object.
The address operator (&) requires an lvalue as an operand while the increment (++) and the decrement (--) operators require a modifiable lvalue as an operand. The following example shows expressions and their corresponding lvalues.
Expression | Lvalue |
---|---|
x = 42 | x |
*ptr = newvalue | *ptr |
a++ | a |
int& f() |
The function call to f() |
The remainder of this section is platform-specific and pertains to C only.
When compiled with the GNU C language extensions enabled, compound expressions, conditional expressions, and casts are allowed as lvalues, provided that their operands are lvalues. The use of this language extension is deprecated for C++ code.
A compound expression can be assigned if the last expression in the sequence is an lvalue. The following expressions are equivalent:
(x + 1, y) *= 42; x + 1, (y *=42);
The address operator can be applied to a compound expression, provided the last expression in the sequence is an lvalue. The following expressions are equivalent:
&(x + 1, y); x + 1, &y;
A conditional expression can be a valid lvalue if its type is not void and both of its branches for true and false are valid lvalues. Casts are valid lvalues if the operand is an lvalue. The primary restriction is that you cannot take the address of an lvalue cast.
---------------------------------------------------
今天看C++模板的资料,里面说到lvalue,rvalue的问题,这个问题以前也看到过,也查过相关资料,但是没有考虑得很深,只知道rvalue不能取地址,不能赋值等等一些规则。今天则突然有了更深层次的理解(也可以说是顿悟,耗时不过几秒钟),记录下来。
下面是我对这两个单词字面的意思的猜测:
接下来是我所悟到内容的详细分析
访问lvalue一定会导致CPU访问存储器(相对较慢的操作)。
lvalue的例子:
rvalue是不可以赋值的,它不是一个变量,在内存中没有存在,没有地址。它要么是存在于CPU的寄存器中,要么是存在于指令中(立即数)。所以只要对rvalue取地址,那么就一定是错误的(编译器会抱怨的)。
访问rvalue不会导致CPU访问存储器(对立即数和寄存器的访问很快)。
rvalue的例子:
带着疑问,我查了查vc编译出来的代码,原来obj这个局部变量的地址被压入了堆栈,foo函数内部以堆栈上的obj地址作为this指针调用了UDT的构造函数。噢,难怪执行UDT obj = foo();这个语句只有调用了一次构造函数,而没有调用operator =,这都是因为函数返回值必须是rvalue这个规则所带来的好处,如果返回值是一个lvalue,那么这个语句一定会调用operator = 运算符。