INTRODUCTION
ISOC++11 (officially ISO/IEC 14882:2011) is the most recent version of the standard of the C++ programming language. It contains some new features, and concepts, for example:
- rvalue references
- xvalue, glvalue, prvalue expression value categories
- move semantics
If we would like to understand the concepts of the new expression value categories we have to be aware of that there are rvalue and lvalue references.
It is better to know rvalues can be passed to non-const rvalue references.
int& r_i=7; // compile error
int&& rr_i=7; // OK
We can gain some intuition of the concepts of value categories if we quote the subsection titled Lvalues and rvalues from the working draft N3337 (the most similar draft to the published ISOC++11 standard).
3.10 Lvalues and rvalues [basic.lval]
1 Expressions are categorized according to the taxonomy in Figure 1.
- An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function
or an object. [ Example: If E is an expression of pointer type, then
*E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function
whose return type is an lvalue reference is an lvalue. —end example ]
- An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for
example). An xvalue is the result of certain kinds of expressions
involving rvalue references (8.3.2). [ Example: The result of calling
a function whose return type is an rvalue reference is an xvalue. —end
example ]
- A glvalue (“generalized” lvalue) is an lvalue or an xvalue.
- An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment expression) is an xvalue, a
temporary object (12.2) or subobject thereof, or a value that is not
associated with an object.
- A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [ Example: The result of calling a function whose return type is not a
reference is a prvalue. The value of a literal such as 12, 7.3e5, or
true is also a prvalue. —end example ]
Every expression belongs to exactly one of the fundamental
classifications in this taxonomy: lvalue, xvalue, or prvalue. This
property of an expression is called its value category.
But I am not quite sure about that this subsection is enough to understand the concepts clearly, because "usually" is not really general, "near the end of its lifetime" is not really concrete, "involving rvalue references" is not really clear, and "Example: The result of calling a function whose return type is an rvalue reference is an xvalue." sounds like a snake is biting its tail.
PRIMARY VALUE CATEGORIES
Every expression belongs to exactly one primary value category. These value categories are lvalue, xvalue and prvalue categories.
lvalues
The expression E belongs to the lvalue category if and only if E refers to an entity that ALREADY has had an identity (address, name or alias) that makes it accessible outside of E.
#include <iostream>
int i=7;
const int& f(){
return i;
}
int main()
{
std::cout<<&"www"<<std::endl; // The expression "www" in this row is an lvalue expression, because string literals are arrays and every array has an address.
i; // The expression i in this row is an lvalue expression, because it refers to the same entity ...
i; // ... as the entity the expression i in this row refers to.
int* p_i=new int(7);
*p_i; // The expression *p_i in this row is an lvalue expression, because it refers to the same entity ...
*p_i; // ... as the entity the expression *p_i in this row refers to.
const int& r_I=7;
r_I; // The expression r_I in this row is an lvalue expression, because it refers to the same entity ...
r_I; // ... as the entity the expression r_I in this row refers to.
f(); // The expression f() in this row is an lvalue expression, because it refers to the same entity ...
i; // ... as the entity the expression f() in this row refers to.
return 0;
}
xvalues
The expression E belongs to the xvalue category if and only if it is
— the result of calling a function, whether implicitly or explicitly, whose return type is an rvalue reference to the type of object being returned, or
int&& f(){
return 3;
}
int main()
{
f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.
return 0;
}
— a cast to an rvalue reference to object type, or
int main()
{
static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.
std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).
return 0;
}
— a class member access expression designating a non-static data member of non-reference type in which the object expression is an xvalue, or
struct As
{
int i;
};
As&& f(){
return As();
}
int main()
{
f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.
return 0;
}
— a pointer-to-member expression in which the first operand is an xvalue and the second operand is a pointer to data member.
Note that the effect of the rules above is that named rvalue references to objects are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not.
#include <functional>
struct As
{
int i;
};
As&& f(){
return As();
}
int main()
{
f(); // The expression f() belongs to the xvalue category, because it refers to an unnamed rvalue reference to object.
As&& rr_a=As();
rr_a; // The expression rr_a belongs to the lvalue category, because it refers to a named rvalue reference to object.
std::ref(f); // The expression std::ref(f) belongs to the lvalue category, because it refers to an rvalue reference to function.
return 0;
}
prvalues
The expression E belongs to the prvalue category if and only if E belongs neither to the lvalue nor to the xvalue category.
struct As
{
void f(){
this; // The expression this is a prvalue expression. Note, that the expression this is not a variable.
}
};
As f(){
return As();
}
int main()
{
f(); // The expression f() belongs to the prvalue category, because it belongs neither to the lvalue nor to the xvalue category.
return 0;
}
MIXED VALUE CATEGORIES
There are two further important mixed value categories. These value categories are rvalue and glvalue categories.
rvalues
The expression E belongs to the rvalue category if and only if E belongs to the xvalue category, or to the prvalue category.
Note that this definition means that the expression E belongs to the rvalue category if and only if E refers to an entity that has not had any identity that makes it accessible outside of E YET.
glvalues
The expression E belongs to the glvalue category if and only if E belongs to the lvalue category, or to the xvalue category.
A PRACTICAL RULE
Scott Meyer has published a very useful rule of thumb to distinguish rvalues from lvalues.
- If you can take the address of an expression, the expression is an lvalue.
- If the type of an expression is an lvalue reference (e.g., T& or const T&, etc.), that expression is an lvalue.
- Otherwise, the expression is an rvalue. Conceptually (and typically also in fact), rvalues correspond to temporary objects, such
as those returned from functions or created through implicit type
conversions. Most literal values (e.g., 10 and 5.3) are also rvalues.
x
wherex
isint
can be used either as an lvalue or an rvalue. – Revulsionstring("hello") = string("world")
. – Zenithalstd::string::operator=(std::string const&)
) on temporaries. But assignment to UDT's is a bit tricky to understand fully. – Checklist