Note of Effective Modern C++
文章最后更新时间为:2023 年 04 月 30 日 19:13:54
Note of Effective Modern Cpp
Rvalue & Rvalue Reference
Rvalue
In C++, an lvalue (locator value) represents an object that has a specific memory location, and an rvalue (right-hand value) represents a temporary object that doesn't have a stable memory location.
Rvalue Reference
An rvalue reference is a type of reference introduced in C++11, which is designed to bind to temporary objects or rvalues. Rvalue references enable move semantics, perfect forwarding, and other performance optimizations in C++ code. Rvalue references are denoted with a double ampersand &&
.
So if rvalue represents a temporary object that doesn’t have a stable memory location. Why does the following code work?
#include <iostream>
#include <utility>
void process_value(int& lvalue_ref) {
std::cout << "Lvalue reference: " << lvalue_ref << std::endl;
}
void process_value(int&& rvalue_ref) {
std::cout << "Rvalue reference: " << rvalue_ref << std::endl;
rvalue_ref = 0; // Modifying the rvalue reference argument
}
int main() {
int a = 42;
process_value(a); // Calls the function with an lvalue reference
process_value(42); // Calls the function with an rvalue reference
process_value(a * 2); // Calls the function with an rvalue reference
process_value(std::move(a)); // Calls the function with an rvalue reference, explicitly casts the lvalue to an rvalue
std::cout << "Value of a after the fourth process_value call: " << a << std::endl;
return 0;
}
Output is:
Lvalue reference: 42
Rvalue reference: 42
Rvalue reference: 84
Rvalue reference: 42
Value of a after the fourth process_value call: 0
Why the value of a
changes after we calls process_value
? It does not seem to satisfy the definition of right-value? How does the cpp implement rvalue reference?
When an rvalue reference is created, it binds to an rvalue, essentially providing a reference to that rvalue. However, if you use std::move()
to cast an lvalue to an rvalue reference, the rvalue reference now binds to that lvalue. This means that any modification to the rvalue reference will also modify the original lvalue since they now refer to the same memory location.
In the C++ implementation, an rvalue reference is just another reference type that binds to a temporary object or an object explicitly cast to an rvalue. The difference between an lvalue reference and an rvalue reference is primarily in their usage and the compiler's assumptions.
The compiler assumes that lvalue references won't change the underlying object unless they are declared with the const
qualifier. (For example, copy constructor.) On the other hand, rvalue references signify that the underlying object can be safely modified, moved, or have its resources taken because it is either a temporary object or an object explicitly cast using std::move()
. (For example, move constructor.)
In the example provided earlier, when we use std::move(a)
, we are explicitly casting the lvalue a
to an rvalue reference. This tells the compiler that it's okay to modify the object referred to by the rvalue reference. Since the rvalue reference in the process_value
function is now bound to the original variable a
, modifying the rvalue reference will also modify the value of a
.
In fact, the above example does not demonstrate the advantages of rvalues and rvalue references very well (integers are very easy to copy and move). However, I hope it can help you understand the differences and intentions between rvalue references and lvalue references.