The standard mentions this:
An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) [...]
An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment expression) [...]
That is, an lvalue was something you could assign to and an rvalue was something you could assign from.
However, this has gradually gotten further and further from the truth. A simple example of an lvalue that you can't assign it is a const
variable.
const int x = 5;
x = 6; // Error
You can even have an rvalue appear on the left side of an assignment when you involve operator overloading.
I find it more useful to think of an lvalue as referencing an object stored in memory and an rvalue as just a value (that may have been read from memory). The concepts mirror this idea quite well. Some examples:
- Lvalue-to-rvalue can be considered the reading of a value from an object in memory.
- Most operators require lvalue-to-rvalue conversion because they use the value of the object to calculate a result.
- The address of operator (
&
) requires an lvalue because you can only take the address of something in memory. It doesn't need to get the value of the object to work out its address. - Performing
std::move
to turn an lvalue expression into an rvalue expression can be thought of as tricking the compiler into thinking the object that's stored in memory is actually just a temporary value.
However, this also doesn't hold up in every situation. It's just a reasonable analogy.