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,
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 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:
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;}
Here’s a detailed explanation of the code above:
Line 8: a
is an lvalue; a
is a variable with a memory location that holds the value 10
.
Line 10: ptr
is an lvalue; ptr
is a variable with a memory location that holds the address of an integer. &a
is an lvalue: The address-of operator (&
) is applied to a
, which results in an lvalue representing the memory location of a
. Next, *ptr
is an lvalue; the dereference operator (*
)is applied to the pointer ptr
, resulting in an lvalue representing the value stored at the memory location pointed to by ptr
.
Line 12: lvalue_ref
is an lvalue reference; lvalue_ref
is a reference to an integer (int&
) that refers to the variable a
.
Line 13: global_ref
is an lvalue reference; global_ref
is a reference to an integer that refers to the global variable global_value
.
Line 15: const_lvalue_ref
is a const lvalue reference; const_lvalue_ref
is a reference to a constant integer (const int&
) that refers to the variable a
.
Line 17: rvalue_ref
is an rvalue reference; rvalue_ref
is a reference to an rvalue (int&&
) that refers to the temporary value 100
.
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
100
while 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 of a
is being moved and can be reused without the need for a deep copy. moved_from
is a reference to an rvalue (int&&
) that refers to the result of std::move(a)
.
Line 21: a
is an lvalue, representing a variable with a memory location. 5
is an rvalue because it represents a constant value without a memory location. The expression a + 5
(prvalue) computes the sum of a
and 5
, resulting in a temporary value without a memory location.
Line 23: v
is a variable with a memory location that holds a std::vector
object.
Line 24: v[2]
is an lvalue; The expression v[2]
accesses the third element of the vector v
, resulting in an lvalue representing the accessed element.
Line 26: arr
is an lvalue; arr
is a pointer variable with a memory location that holds the address of dynamically allocated memory. new int[4]
is an rvalue; The expression new int[4]
allocates a dynamic array of integers and produces a temporary value without a memory location.
Line 33: delete[] arr
is a prvalue; The expression delete[] arr
deallocates the dynamically allocated memory pointed to by arr
, but it doesn't produce a value. It has an effect but doesn't return a result.
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