What are rvalues, lvalues, xvalues, glvalues, and prvalues?
C++11 introduced rvalues, lvalues, xvalues, glvalues, and prvalues. Understanding these concepts is important for working with values in C++.
These are defined below:
lvalue (Left or locator value expression): An lvalue (left value) is an expression that refers to a memory location that our program can access. In simpler terms, an lvalue represents an object with a specific memory location, and we can assign a new value to it. Examples of lvalues include variables,
, and elements of an array.non-const references A non-const reference is a reference that allows the referred object to be modified through that reference. rvalue (Right value expression): An rvalue (right value) is an expression that represents a value that does not have a memory location assigned to it. These are temporary values and are typically used for computation purposes. We cannot assign a new value to an rvalue. Examples of rvalues include literals (like numeric or string literals), temporary objects, and the result of an expression.
xvalue (eXpiring Value): An xvalue is a subcategory of an rvalue. An xvalue expression has a memory address not accessible by our program but can be used to initialize an rvalue. Xvalues are typically used in
, allowing efficient resource transfer without unnecessary copying. Examples of xvalues include the result of invokingmove semantics The ability to efficiently transfer resources such as memory ownership from one object to another without unnecessary copying. std::move()on an lvalue or accessing the member of an object that is about to be destroyed.prvalue (Pure rvalue): A prvalue is a subcategory of an rvalue. A prvalue expression does not have a memory address accessible by our program. It is a value that can be used to compute something. Examples of prvalues include numeric literals, string literals, and the results of arithmetic operations.
glvalue (Generalized lvalue): A glvalue includes both lvalues and xvalues. It refers to an expression that can be used as an lvalue or produce an xvalue. In other words, glvalues represent expressions with a specific memory location and can potentially be used for resource transfer.
The illustration below shows the categorization of the above concept:
Example
The following code demonstrates different value types in C++:
#include <iostream>#include <vector>#include <utility>int global_value = 100;int main() {int a = 10; // 'a' is an lvalueint* ptr = &a; // 'ptr' is an lvalue, '&a' is an lvalue, and '*ptr' is an lvalueint& lvalue_ref = a; // 'lvalue_ref' is an lvalue reference and 'a' is an lvalueint& global_ref = global_value; // 'global_ref' is an lvalue reference and 'global_value' is an lvalueconst int& const_lvalue_ref = a; // 'const_lvalue_ref' is a const lvalue reference and 'a' is an lvalueint&& rvalue_ref = 100; // 'rvalue_ref' is an rvalue referencea and '100' is an rvalueint&& moved_from = std::move(a); // 'std::move(a)' is an xvalue and 'moved_from' is an rvalue referenceint result = a + 5; // 'a' is an lvalue, '5' is an rvalue, 'a + 5' is a prvalue, and 'result' is an lvaluestd::vector<int> v = {1, 2, 3, 4,5}; // 'v' is an lvalueint vec_element = v[2]; // 'vec_element' is an lvalue and 'v[2]' is an lvalueint* arr = new int[4]; // 'arr' is an lvalue and 'new int[4]' is an rvaluestd::cout << "a: " << a << ", ptr: " << *ptr << ", lvalue_ref: " << lvalue_ref << std::endl;std::cout << "global_ref: " << global_ref << ", const_lvalue_ref: " << const_lvalue_ref << std::endl;std::cout << "rvalue_ref: " << rvalue_ref << ", moved_from: " << moved_from << std::endl;std::cout << "result: " << result << ", vec_element: " << vec_element << std::endl;delete[] arr; // 'arr' is an lvalue and 'delete[] arr' is a prvaluereturn 0;}
Explanation
Here’s a detailed explanation of the code above:
Line 8:
ais an lvalue;ais a variable with a memory location that holds the value10.Line 10:
ptris an lvalue;ptris a variable with a memory location that holds the address of an integer.&ais an lvalue: The address-of operator (&) is applied toa, which results in an lvalue representing the memory location ofa. Next,*ptris an lvalue; the dereference operator (*)is applied to the pointerptr, resulting in an lvalue representing the value stored at the memory location pointed to byptr.Line 12:
lvalue_refis an lvalue reference;lvalue_refis a reference to an integer (int&) that refers to the variablea.Line 13:
global_refis an lvalue reference;global_refis a reference to an integer that refers to the global variableglobal_value.Line 15:
const_lvalue_refis a const lvalue reference;const_lvalue_refis a reference to a constant integer (const int&) that refers to the variablea.Line 17:
rvalue_refis an rvalue reference;rvalue_refis a reference to an rvalue (int&&) that refers to the temporary value100.
Note: An rvalue is a temporary value that doesn’t have a persistent memory location. It’s typically a value that’s not associated with a named variable like
100while rvalue references are used to bind to temporary objects or rvalues. They extend the lifetime of temporary objects.
Line 19:
std::move(a)is an xvalue;std::move(a)produces an xvalue, indicating that the value ofais being moved and can be reused without the need for a deep copy.moved_fromis a reference to an rvalue (int&&) that refers to the result ofstd::move(a).Line 21:
ais an lvalue, representing a variable with a memory location.5is an rvalue because it represents a constant value without a memory location. The expressiona + 5(prvalue) computes the sum ofaand5, resulting in a temporary value without a memory location.Line 23:
vis a variable with a memory location that holds astd::vectorobject.Line 24:
v[2]is an lvalue; The expressionv[2]accesses the third element of the vectorv, resulting in an lvalue representing the accessed element.Line 26:
arris an lvalue;arris a pointer variable with a memory location that holds the address of dynamically allocated memory.new int[4]is an rvalue; The expressionnew int[4]allocates a dynamic array of integers and produces a temporary value without a memory location.Line 33:
delete[] arris a prvalue; The expressiondelete[] arrdeallocates the dynamically allocated memory pointed to byarr, but it doesn't produce a value. It has an effect but doesn't return a result.
Conclusion
The concepts of rvalues, lvalues, xvalues, glvalues, and prvalues is crucial for effective programming in C++. These terms categorize different types of expressions and values within the language. Understanding these distinctions facilitates more efficient resource management, especially in areas like move semantics and avoiding unnecessary copying, thereby enhancing the overall performance and clarity of C++ code.
Free Resources