C++ Rvalue References
In this page:
data_type &&reference_name = temporary_value;
void function_name(data_type &¶m) { } // accepts temporaries
Lvalues vs. Rvalues
An lvalue is an expression that refers to a persistent object with an identifiable memory location, such as a named variable -- you can take its address.
An rvalue is a temporary value, like a literal (5) or the return value of a function, which exists only briefly and has no stable address you can rely on.
Example: Lvalues vs. Rvalues
// Include std::cout and std::cin
#include <iostream>
// Program execution starts in main()
int main() {
// Declare x and set it to 5
int x = 5;
// Declare y and set it to x + 1
int y = x + 1;
// Print to the console with cout
std::cout << y << std::endl;
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Rvalue Reference Syntax (&&)
An rvalue reference is declared with a double ampersand (T&&) after the type, and specifically binds to temporaries rather than named variables.
This distinct reference type is what lets a function overload detect, at compile time, whether it was handed a temporary that's safe to "steal" resources from.
Example: Rvalue Reference Syntax (&&)
// Include std::cout and std::cin
#include <iostream>
void show(int &&x) {
// Print to the console with cout
std::cout << "Rvalue reference: " << x << std::endl;
}
// Program execution starts in main()
int main() {
show(5);
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Binding Constraints
A regular lvalue reference (T&) can only bind to an lvalue -- it can't bind to a temporary at all, since the temporary might be destroyed before the reference could safely be used.
Conversely, an rvalue reference (T&&) can't bind to a named variable directly, since that variable is still in active use elsewhere.
Example: Binding Constraints
// Include std::cout and std::cin
#include <iostream>
void show(int &x) { std::cout << "lvalue ref" << std::endl; }
void show(int &&x) { std::cout << "rvalue ref" << std::endl; }
// Program execution starts in main()
int main() {
// Declare a and set it to 5
int a = 5;
show(a);
show(10);
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Casting Lvalues with std::move
std::move() exists specifically to bridge this gap: it casts an lvalue to an rvalue reference, letting you explicitly tell the compiler "I'm done with this variable, treat it as a temporary you're free to move from" even though it has a name and an address.
Example: Casting Lvalues with std::move
// Include std::cout and std::cin
#include <iostream>
#include <utility>
void show(int &&x) { std::cout << "rvalue: " << x << std::endl; }
// Program execution starts in main()
int main() {
// Declare a and set it to 5
int a = 5;
// std::move transfers the resource instead of copying it
show(std::move(a));
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Overloading with Rvalue References
When both an lvalue-reference and an rvalue-reference overload of a function exist, the compiler automatically picks the rvalue overload for temporaries and function-return values, which is exactly what enables move semantics to kick in transparently without you writing any extra code at the call site.
Example: Overloading with Rvalue References
// Include std::cout and std::cin
#include <iostream>
#include <utility>
void process(const int &x) { std::cout << "lvalue overload" << std::endl; }
void process(int &&x) { std::cout << "rvalue overload" << std::endl; }
// Program execution starts in main()
int main() {
// Declare a and set it to 5
int a = 5;
process(a);
process(10);
// std::move transfers the resource instead of copying it
process(std::move(a));
// Return 0 to signal that the program finished successfully
return 0;
}
Login to try C/C++/Java/PHP code in the editor
- Trying to bind an rvalue reference
int&&to a named variable withoutstd::move, which does not compile. - Using an object after passing it to
std::move, when its value may be unspecified. - Assuming
std::moveitself moves anything, when it only casts to an rvalue and the move happens in the constructor or assignment.
Chapter Quiz — Complete all 9 topics to unlock
0/9 topics done
Complete these topics first: