The rvalue and lvalue attributes apply to expressions, not to objects. An expression can be either an lvalue or an rvalue. Oversimplifying a expression that yields a value is an rvalue-expression and an expression that yields an object is an lvalue-expression. The lvalue to rvalue conversion is the act of reading the value out of an object.
A expression that yields a temporary and a literal are both rvalue-expressions they represent a value not an actual object.
In your example:
const int &ri = 2 + 3;
const int *pi = &ri;
The expression 2+3
is an rvalue-expression used to initialize a constant reference. That, according to the language implies the extension of the lifetime of the temporary beyond the current expression and until the reference goes out of scope. After that, in the second expression, the subexpression ri
is an lvalue-expression that refers to the temporary object, whose lifetime has been extended.
Note that there are other ways of creating rvalue expressions with temporaries, for example calling a member that yields a reference:
struct test {
test& thisTest() { return *this; }
};
test foo();
... foo().thisTest()
The subexpression foo()
is an rvalue-expression, but the expression foo().thisTest()
is an lvalue-expression. Both of them refer to a temporary object that will disappear at the end of the full expression.