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

C++ Rvalue References

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 <iostream>

int main() {
	int x = 5;
	int y = x + 1;
	std::cout << y << std::endl;
	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 <iostream>

void show(int &&x) {
	std::cout << "Rvalue reference: " << x << std::endl;
}

int main() {
	show(5);
	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 <iostream>

void show(int &x) { std::cout << "lvalue ref" << std::endl; }
void show(int &&x) { std::cout << "rvalue ref" << std::endl; }

int main() {
	int a = 5;
	show(a);
	show(10);
	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 <iostream>
#include <utility>

void show(int &&x) { std::cout << "rvalue: " << x << std::endl; }

int main() {
	int a = 5;
	show(std::move(a));
	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 <iostream>
#include <utility>

void process(const int &x) { std::cout << "lvalue overload" << std::endl; }
void process(int &&x) { std::cout << "rvalue overload" << std::endl; }

int main() {
	int a = 5;
	process(a);
	process(10);
	process(std::move(a));
	return 0;
}

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.