← Back to C++ Course | Chapter 16: Modern C++ | Lesson 5 of 9

C++ Rvalue References

An rvalue reference is a way to grab hold of a temporary value that is about to disappear, like catching a ball before it is gone. It makes moving instead of copying possible.
Syntax
cpp
data_type &&reference_name = temporary_value;

void function_name(data_type &&param) { }  // 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

cpp
// 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;
}

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 (&&)

cpp
// 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;
}

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

cpp
// 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;
}

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

cpp
// 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;
}

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

cpp
// 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;
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Trying to bind an rvalue reference int&& to a named variable without std::move, which does not compile.
  2. Using an object after passing it to std::move, when its value may be unspecified.
  3. Assuming std::move itself moves anything, when it only casts to an rvalue and the move happens in the constructor or assignment.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.