3.1 Lvalues and Rvalues

Lvalues and rvalues are fundamental to C++ expressions. Put simply, an lvalue is an object reference and an rvalue is a value. The difference between lvalues and rvalues plays a role in the writing and understanding of expressions.

An lvalue is an expression that yields an object reference, such as a variable name, an array subscript reference, a dereferenced pointer, or a function call that returns a reference. An lvalue always has a defined region of storage, so you can take its address.

An rvalue is an expression that is not an lvalue. Examples of rvalues include literals, the results of most operators, and function calls that return nonreferences. An rvalue does not necessarily have any storage associated with it.

Strictly speaking, a function is an lvalue, but the only uses for it are to use it in calling the function, or determining the function's address. Most of the time, the term lvalue means object lvalue, and this book follows that convention.

C++ borrows the term lvalue from C, where only an lvalue can be used on the left side of an assignment statement. The term rvalue is a logical counterpart for an expression that can be used only on the righthand side of an assignment. For example:

#define rvalue 42

int lvalue;

lvalue = rvalue;

In C++, these simple rules are no longer true, but the names remain because they are close to the truth. The most significant departure from traditional C is that an lvalue in C++ might be const, in which case it cannot be the target of an assignment. (C has since evolved and now has const lvalues.)

The built-in assignment operators require an lvalue as their lefthand operand. The built-in address (&) operator also requires an lvalue operand, as do the increment (++) and decrement (--) operators. Other operators require rvalues. The rules are not as strict for user-defined operators. Any object, including an rvalue, can be used to call member functions, including overloaded =, &, ++, and -- operators.

Some other rules for lvalues and rvalues are:

  • An array is an lvalue, but an address is an rvalue.

  • The built-in array subscript ([]), dereference (unary *), assignment (=, +=, etc.), increment (++), and decrement (--) operators produce lvalues. Other built-in operators produce rvalues.

  • A type cast to a reference type produces an lvalue; other casts produce rvalues.

  • A function call (including overloaded operators) that returns a reference returns an lvalue; otherwise, it returns an rvalue.

  • An lvalue is converted implicitly to an rvalue when necessary, but an rvalue cannot be implicitly converted to an lvalue.

Example 3-1 shows several different kinds of lvalues and rvalues.

Example 3-1. Lvalues and rvalues
class number {

public:

  number(int i = 0) : value(i) {}

  operator int(  ) const { return value; }

  number& operator=(const number& n);

private:

  int value;

};



number operator+(const number& x, const number& y);



int main(  )

{

  number a[10], b(42);

  number* p;

  a;           // lvalue

  a[0];        // lvalue

  &a[0];       // rvalue

  *a;          // lvalue

  p;           // lvalue

  *p;          // lvalue

  10;          // rvalue

  number(10);  // rvalue

  a[0] + b;    // rvalue

  b = a[0];    // lvalue

}