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, non-const referencesA non-const reference is a reference that allows the referred object to be modified through that reference. , and elements of an array.

  • 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 move semanticsThe ability to efficiently transfer resources such as memory ownership from one object to another without unnecessary copying., allowing efficient resource transfer without unnecessary copying. Examples of xvalues include the result of invoking 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:

Categorization of expression in C++
Categorization of expression in C++

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 lvalue
int* ptr = &a; // 'ptr' is an lvalue, '&a' is an lvalue, and '*ptr' is an lvalue
int& lvalue_ref = a; // 'lvalue_ref' is an lvalue reference and 'a' is an lvalue
int& global_ref = global_value; // 'global_ref' is an lvalue reference and 'global_value' is an lvalue
const int& const_lvalue_ref = a; // 'const_lvalue_ref' is a const lvalue reference and 'a' is an lvalue
int&& rvalue_ref = 100; // 'rvalue_ref' is an rvalue referencea and '100' is an rvalue
int&& moved_from = std::move(a); // 'std::move(a)' is an xvalue and 'moved_from' is an rvalue reference
int result = a + 5; // 'a' is an lvalue, '5' is an rvalue, 'a + 5' is a prvalue, and 'result' is an lvalue
std::vector<int> v = {1, 2, 3, 4,5}; // 'v' is an lvalue
int vec_element = v[2]; // 'vec_element' is an lvalue and 'v[2]' is an lvalue
int* arr = new int[4]; // 'arr' is an lvalue and 'new int[4]' is an rvalue
std::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 prvalue
return 0;
}

Explanation

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.

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.

Copyright ©2024 Educative, Inc. All rights reserved