Quick summary ↬ C++ had value categories in C++0X version. The two value categories that were before C++11 are lvalues and rvalues. One of the greatest addition to C++11 was the introduction of movable types. Learning about the value categories is one of the pre-requisites to learn about move semantics. Any expression before the C++11 standard was either lvalue or rvalue. lvalue has an identity and its lifetime depends on the scope of the variable. On the other hand, rvalues don’t have an identity and it does not live beyond the life of the expression.
1. Introduction
Let’s consider few examples to understand l-value and the r-values in depth:
|
|
As already said, before C++11, expressions are categorized as lvalues or rvalues. They are categorized based on the identity of the expression. But, it all changed after the C++11 standard version. Expressions are now categorized based on identity and movability. Below is the short definition for all the value categories.
x-values -> “It’s abbreviated as ‘Expiring values’. Refers to objects that nears the end of it’s lifetime. Example: Result of calling a function that returns r-value reference is an x-value. It has an identity and it’s movable."
l-values -> “It has it’s own identity name. It lives till the end of the block either global or local variable."
gl-values -> “Abbreviated as ‘Genaralized l-value’. Its an l-value or an x-value."
r-values -> “An r-value appears on the right hand side of the expression and it’s an x-value or a temporary object or a sub-object or an value that is not associated with an object.”
pr-values -> “It’s abbreviated as ‘pure r-values’. It has no identity and it’s movable."
2. x-values
If an expression states: “a reference to T” then, the expression denotes an object or function denoted by the reference, and the expression is an lvalue or an x-value, depending on the expression. An expression is an x-value in the below-given scenarios:
2.1 Scenario 1: The result of calling a function whether explicitly or implicitly, whose return type is an rvalue reference to object type.
Let’s consider the below example where a function returns an rvalue reference and makes the expression an x-value.
|
|
In the preceding example, the functions balloon() and kite() returns rvalue references and that makes the calling expressions x-values. As the name denotes, If not returned, the rvalues declared inside the function will expire inside the functions but now we have it returned to the calling expressions in the main function. This behavior makes the calling expression an x-value (eXpiring values). We have extended the expiry of those rvalues by passing the references to the calling expression respectively.
2.2 Scenario 2: A cast to an rvalue reference to an object type
Casting an rvalue reference to an object type leads to an x-value expression. Here is an example:
|
|
In the preceding example, we do static_cast to store the address of an rvalue to a variable thus creating an rvalue reference. The whole expression is called an xvalue because it increases the lifetime of the rvalue beyond the declaration of it.
2.3 Scenario 3: A class member access expression designating a non-static data member in which the object expression is an xvalue
In this scenario, we need to write an expression that accesses the class data member that is not static. There are some factors which we need to consider while writing the expression. To understand that, we need to know how the “class member access expression” looks like. It has two different parts, an object expression part (The part of expression before the dot operator) and the member identification part (The part of expression after the dot operator). The object expression part of the expression should be an xvalue and the member identification part should be a non-static member of the struct. In the below example, f().m; is an x-value expression because it satisfies the conditions we just discussed above.
|
|
2.4 Scenario 4: A .* pointer-to-member expression in which the first operand is an xvalue and the second operand is a pointer to data member
In this scenario, we need to write an expression that has xvalue as the first operand and it accesses the pointer to the data member declared inside the struct or class. Below is the example that does this.
|
|
In the above example, we have declared an xvalue expression get_xvalue().*p; in which the first operand get_xvalue() is an method that returns the rvalue reference to the object of struct C. Thus the calling expression in the main() becomes an xvalue because it increases the lifetime of an rvalue. This xvalue is used to access the *p thus makes the whole expression an xvalue.