CppRvalue References

Rvalue References

An rvalue reference, written with a double ampersand T&&, is a reference type that can only bind to rvalues — temporaries and other expressions that don't have a persistent name. It is the language feature that makes move semantics possible: a function parameter of type T&& signals "I am willing to take ownership of this value's resources, because whatever is bound here is about to be destroyed anyway."

Declaration

Binds to

Meaning

T& x

lvalues only

A regular reference to an existing, named object

const T& x

lvalues and rvalues

Read-only view; the classic way to accept "anything" before C++11

T&& x

rvalues only

An rvalue reference; signals the callee may steal x's resources

Overloading on lvalue-ref vs rvalue-ref
You can overload a function on whether its argument is an lvalue or an rvalue. The compiler picks the rvalue-reference overload whenever the argument is a temporary (or has been cast to an rvalue with std::move), and falls back to the lvalue overload for named variables.

Overload resolution: which version gets called?

CPP
#include <iostream>
#include <string>

void process(const std::string& s) {
    std::cout << "lvalue overload (copying): " << s << "\n";
}

void process(std::string&& s) {
    std::cout << "rvalue overload (can steal): " << s << "\n";
}

std::string makeGreeting() {
    return "hello";
}

int main() {
    std::string name = "Ada";

    process(name);              // lvalue -> const std::string& overload
    process(makeGreeting());    // temporary -> std::string&& overload
    process(std::move(name));   // cast to rvalue -> std::string&& overload

    return 0;
}
Universal (forwarding) references
There is a syntactically identical-looking but semantically different construct: inside a function template, a parameter written as T&& where T is itself a deduced template parameter is not a plain rvalue reference — it is a universal reference (also called a forwarding reference). Depending on what is passed in, T is deduced differently so that the parameter can bind to either an lvalue or an rvalue.

A universal reference and std::forward

CPP
#include <iostream>
#include <utility>

template <typename T>
void relay(T&& x) {
    // std::forward preserves whether the original argument was
    // an lvalue or an rvalue when passing it along.
    process(std::forward<T>(x));
}

// (Assume 'process' overloads exist as in the example above.)
Don't conflate a plain rvalue reference with a universal reference
void f(std::string&& s) is a plain rvalue reference — std::string is a fixed, concrete type, so f only binds to rvalues. template<typename T> void f(T&& x) looks the same but is a universal reference, because T is deduced from the call site — it can bind to lvalues or rvalues depending on what's passed. This distinction matters: universal references almost always need to be paired with std::forward to correctly pass along the value category of the argument (this pattern is called perfect forwarding), whereas a plain rvalue reference parameter is already known to be bound to an rvalue and is typically consumed with std::move instead.
  • T&& declares an rvalue reference, which binds only to temporaries.

  • Overloading on T& vs T&& lets a function behave differently for named variables vs temporaries.

  • template<typename T> void f(T&& x) is a distinct, more advanced feature: a universal/forwarding reference.

  • std::forward preserves the original value category of a forwarded argument in generic code (perfect forwarding).