Question

What T&& Means in C++11: Rvalue References Explained

cppc++11rvalue-referencec++-faqperfect-forwarding

Question

I have been exploring the new features introduced in C++11, and I noticed declarations such as T&& var.

What is this syntax called?

What does it mean exactly?

At first glance, it looks like a “double reference,” similar to how C-style code can use double pointers like T** var, but I am struggling to understand its purpose and when it would be used.

Short Answer

By the end of this page, you will understand that T&& in C++11 usually means an rvalue reference. You will learn what rvalues are, why rvalue references were added, how they enable move semantics and perfect forwarding, and how they differ from ordinary references like T& and const T&.

Concept

In C++11, T&& is called an rvalue reference.

To understand it, you first need to know that C++ expressions are often divided into two broad categories:

  • lvalues: named objects that have a stable location in memory
  • rvalues: temporary values, literals, or objects about to expire

For example:

int x = 10;   // x is an lvalue
int y = x;    // x is still an lvalue
int z = 5;    // 5 is an rvalue

Before C++11, C++ had normal references like T& and const T&:

  • T& binds to modifiable lvalues
  • const T& can bind to both lvalues and rvalues

C++11 added T&& so code can specifically bind to rvalues.

This matters because rvalues are often temporary objects that will not be used again. Since they are about to disappear, C++ can safely move their resources instead of copying them.

That is the main reason T&& exists:

  • to support move semantics
  • to avoid unnecessary copies
  • to improve performance
  • to enable perfect forwarding in templates

A classic example is a std::string. Copying a large string may require allocating memory and copying characters. Moving it can simply transfer ownership of its internal buffer.

std::string a = "hello";
std::string b = a;            // copy
std::string c = std::move(a); // move

In the move case, std::move(a) turns a into an rvalue-like expression, which lets c take over its internal data.

So T&& is not a “reference to a reference” in the everyday sense. It is a distinct language feature for binding to temporary or expiring objects.

Mental Model

Think of C++ objects like physical containers.

  • An lvalue is a labeled storage box sitting on a shelf. It has a name and a location.
  • An rvalue is a temporary package that just arrived and has not been put on a shelf.

A normal reference T& is like saying, “Give me access to that named box on the shelf.”

An rvalue reference T&& is like saying, “This temporary package is about to be discarded anyway, so I am allowed to take its contents instead of making a full copy.”

That is why T&& is so useful for performance. If the object is temporary, C++ can often reuse its internals rather than duplicate them.

Take Quiz

Syntax and Examples

Basic syntax

T&& name = expression;

This usually means name is an rvalue reference, and expression must be an rvalue.

Simple example

int&& a = 5;

This works because 5 is an rvalue.

But this does not work:

int x = 5;
int&& a = x; // error: x is an lvalue

Even though x stores a value, the expression x itself is an lvalue because it has a name.

Using std::move

#include <iostream>
#include <string>
#include <utility>

int main() {
    std::string s1 = "hello";
    std::string s2 = std::move(s1);

    std::cout << "s2 = " << s2 << '\n';
}

Explanation

  • s1 is an lvalue because it has a name.
  • std::move(s1) converts it to an rvalue expression.
  • That allows move construction of s2.

Function overloads with T& and T&&

#include <iostream>

void process(int& x) {
    std::cout << "lvalue reference\n";
}

void process(int&& x) {
    std::cout << "rvalue reference\n";
}

int main() {
    int a = 10;
    process(a);   // calls int& version
    process(20);  // calls int&& version
}

This is one of the clearest ways to see the difference.

  • a is an lvalue
  • 20 is an rvalue

Step by Step Execution

Consider this example:

#include <iostream>
#include <utility>
#include <string>

void print(std::string& s) {
    std::cout << "lvalue: " << s << '\n';
}

void print(std::string&& s) {
    std::cout << "rvalue: " << s << '\n';
}

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

    print(name);
    print(std::string("Bob"));
    print(std::move(name));
}

Step-by-step

  1. std::string name = "Alice";

    • Creates a named string object.
    • name is an lvalue.
  2. print(name);

    • Since name is an lvalue, the compiler chooses:
    • void print(std::string& s)
      
  3. print(std::string("Bob"));

    • std::string("Bob") creates a temporary object.
    • Temporary objects are rvalues.
    • The compiler chooses:
    • void print(std::string&& s)
      
  4. print(std::move(name));

    • name is normally an lvalue.
    • std::move(name) converts it into an rvalue expression.
    • The compiler chooses the std::string&& overload.

Important detail

Inside the function, even a parameter declared as T&& has a name, so the parameter itself is an lvalue when used in expressions.

That is why move constructors and forwarding code often use std::move(param) again inside the function body.

Real World Use Cases

Rvalue references appear in many real C++ programs.

1. Move constructors and move assignment

Classes that manage resources such as memory, file handles, sockets, or buffers often define move operations:

class Buffer {
public:
    Buffer(Buffer&& other);
    Buffer& operator=(Buffer&& other);
};

This allows efficient transfer of ownership instead of expensive copying.

2. Standard library containers

Containers such as std::vector use move semantics when growing or rearranging elements. This can make operations much faster for movable types.

3. Returning objects from functions

Functions often return objects by value:

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

C++ can move the returned object efficiently when needed.

4. Perfect forwarding in generic code

Template utilities such as std::make_unique and emplace_back rely on T&& in a special template context to forward arguments efficiently.

5. Avoiding copies in APIs

Functions that consume ownership may accept rvalue references:

void set_name(std::string&& name);

This signals that the function may move from the argument.

Take Quiz

Real Codebase Usage

In real projects, developers usually use T&& in a few important patterns.

Move constructors

class FileWrapper {
public:
    FileWrapper(FileWrapper&& other) noexcept {
        handle = other.handle;
        other.handle = nullptr;
    }

private:
    void* handle = nullptr;
};

This pattern transfers ownership and leaves the source object in a valid but moved-from state.

Move assignment

class FileWrapper {
public:
    FileWrapper& operator=(FileWrapper&& other) noexcept {
        if (this != &other) {
            handle = other.handle;
            other.handle = nullptr;
        }
        return *this;
    }

private:
    void* handle = nullptr;
};

Overloads for performance

void set_value(const std::string& s); // copy or read
void set_value(std::string&& s);      // move

This is common in classes that store strings, vectors, or other expensive objects.

Perfect forwarding

template <typename T>
void wrapper(T&& value) {
    process(std::forward<T>(value));
}

Here, T&& is special. In a template with type deduction, this can become a forwarding reference.

  • if passed an lvalue, it behaves like an lvalue reference
  • if passed an rvalue, it behaves like an rvalue reference

This is heavily used in generic libraries.

Guarding moved-from objects

After moving from an object, code should still leave it valid. Real code often resets pointers, clears handles, or sets safe default values.

Common Mistakes

1. Thinking T&& means “reference to reference”

It does not mean the same thing as a double pointer like T**.

T&& is a language feature for rvalue references.

2. Assuming a named T&& variable is still an rvalue

This is one of the most common mistakes.

void f(std::string&& s) {
    g(s); // s is an lvalue here because it has a name
}

If you want to pass it onward as an rvalue, use:

void f(std::string&& s) {
    g(std::move(s));
}

3. Using std::move too early

std::string name = "Alice";
std::string other = std::move(name);
std::cout << name; // valid, but value is unspecified

A moved-from object is valid, but you should not assume it still contains its old value.

4. Trying to bind T&& directly to an lvalue

Broken code:

int x = 10;
int&& r = x; // error

Correct version:

int x = 10;
int&& r = std::move(x);

5. Forgetting std::forward in forwarding code

Broken pattern:

template <typename T>
void wrapper(T&& value) {
    process(value); // always passes as lvalue
}

Correct pattern:

template <typename T>
void wrapper(T&& value) {
    process(std::forward<T>(value));
}

6. Writing move operations when the defaults are enough

Sometimes the compiler-generated move constructor and move assignment operator are already correct. Writing custom move code when it is unnecessary can introduce bugs.

Comparisons

ConceptBinds to lvalues?Binds to rvalues?Typical use
T&YesNoModify a named object
const T&YesYesRead without copying
T&&No, not directlyYesMove from temporaries or expiring objects

T& vs T&&

int x = 1;
int& a = x;   // OK
int&& b = 2;  // OK
int&& c = x;  // Error
  • T& is for normal named objects.
  • T&& is for temporary or explicitly moved objects.

const T& vs T&&

const T& can bind to temporaries too:

const std::string& a = std::string("hello");

So why use T&&?

  • const T& lets you observe a temporary
  • T&& lets you modify or move from it

Rvalue reference vs forwarding reference

template <typename T>
void f(T&& x);

This looks like an rvalue reference, but when T is deduced, it is a forwarding reference.

That means it can bind to both lvalues and rvalues depending on the call.

By contrast:

void f(std::string&& x);

This is always a plain rvalue reference.

Take Quiz

Cheat Sheet

Quick definition

  • T&& usually means rvalue reference
  • Used for move semantics and perfect forwarding

Value categories

  • lvalue: named object, stable identity
  • rvalue: temporary value or expiring object

Binding rules

T&        -> binds to non-const lvalues
const T&  -> binds to lvalues and rvalues
T&&       -> binds to rvalues

Examples

int x = 10;
int& a = x;            // OK
const int& b = 5;      // OK
int&& c = 5;           // OK
int&& d = x;           // Error
int&& e = std::move(x); // OK

Important rule

A named rvalue reference is an lvalue in expressions:

void f(std::string&& s) {
    use(s);            // s is lvalue here
    use(std::move(s)); // now treated as rvalue
}

Common tools

  • std::move(x) casts to an rvalue expression
  • std::forward<T>(x) preserves lvalue/rvalue category in templates

Typical uses

  • move constructor
  • move assignment operator
  • overloads for expensive objects
  • perfect forwarding in templates

Safe expectation after move

  • moved-from object is valid
  • its exact content is usually unspecified

FAQ

What is T&& called in C++?

It is usually called an rvalue reference.

Is T&& a double reference?

No. It is not like a double pointer such as T**. It is a separate C++ language feature.

Why was T&& added to C++11?

It was added mainly to support move semantics and perfect forwarding, which improve performance and flexibility.

Can T&& bind to a normal variable?

Not directly. A named variable is usually an lvalue. You would need std::move(variable) to treat it as an rvalue.

What is the difference between const T& and T&&?

const T& can read from both lvalues and rvalues without copying. T&& is used when you want to move from or modify an expiring object.

Why is a named T&& parameter treated as an lvalue inside a function?

Because any named variable is an lvalue expression, regardless of its type.

What is std::move actually doing?

It does not move by itself. It converts an expression into an rvalue so move operations can be selected.

Is T&& always an rvalue reference in templates?

No. In a deduced template context like template<typename T> void f(T&& x), it becomes a forwarding reference.

Take Quiz

Mini Project

Description

Build a small class that manages a dynamic array and supports both copying and moving. This project demonstrates why rvalue references matter: copying duplicates data, while moving transfers ownership efficiently.

Goal

Create a resource-owning class with a move constructor and move assignment operator, then test the difference between copy and move behavior.

Requirements

  1. Create a class that allocates an array with new[] and frees it in the destructor.
  2. Add a copy constructor that performs a deep copy.
  3. Add a move constructor that transfers the pointer and resets the source object.
  4. Add a move assignment operator that safely transfers ownership.
  5. Write a main() function that shows copy and move operations in action.
Take Quiz

Keep learning

Related questions

View more