Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save MangaD/f9363edf5ccdee8df931c225cdd3fabd to your computer and use it in GitHub Desktop.

Select an option

Save MangaD/f9363edf5ccdee8df931c225cdd3fabd to your computer and use it in GitHub Desktop.
C++ Forwarding Reference vs. Rvalue Reference: What's the Difference?

C++ Forwarding Reference vs. Rvalue Reference: What's the Difference?

CC0

Disclaimer: ChatGPT generated document.

The terms forwarding reference and rvalue reference are closely related because they both use && syntax, but they behave differently depending on the context.

Feature Rvalue Reference Forwarding Reference
Syntax T&& T&&
Requires type deduction? No Yes
Can bind to lvalues? No Yes (after deduction)
Main purpose Move semantics Perfect forwarding

Rvalue reference

A normal rvalue reference binds only to rvalues (temporary objects or objects explicitly cast with std::move).

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

int main()
{
    std::string str = "hello";

    foo(std::string("world"));   // OK
    foo(std::move(str));         // OK
    // foo(str);                 // Error: lvalue
}

Here std::string&& is not deduced. It always means "rvalue reference to std::string."


Forwarding reference

A forwarding reference occurs only when the type is deduced.

Example:

template<typename T>
void foo(T&& x)
{
    // x is a forwarding reference
}

Now T depends on the argument.

Calling with an lvalue

std::string s = "hello";

foo(s);

Type deduction gives

T = std::string&

So the parameter becomes

std::string& &&   // reference collapsing

which collapses to

std::string&

So x is actually an lvalue reference.


Calling with an rvalue

foo(std::string("hello"));

Now

T = std::string

Parameter becomes

std::string&&

So x is an rvalue reference.


Why is it called a forwarding reference?

Because it preserves the value category when forwarded.

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

If arg was originally

  • an lvalue → forwarded as an lvalue
  • an rvalue → forwarded as an rvalue

Without std::forward:

target(arg);

arg is a named variable, so it is always an lvalue inside the function.


Reference collapsing

Forwarding references rely on reference collapsing:

&  +  &  -> &
&  + &&  -> &
&& +  &  -> &
&& + &&  -> &&

Only && && remains an rvalue reference.


When is T&& not a forwarding reference?

1. No type deduction

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

This is an ordinary rvalue reference.


2. Template parameter is not deduced

template<typename T>
struct A
{
    void foo(T&& x);   // NOT forwarding reference
};

Here T is already fixed when an A<T> is instantiated, so there is no deduction during the call.

Example:

A<std::string> a;

std::string s;
a.foo(std::move(s));   // OK
// a.foo(s);           // Error

3. const T&&

template<typename T>
void foo(const T&& x);

This is not a forwarding reference because the parameter is not exactly T&&.


Generic lambdas

A lambda parameter declared as auto&& is also a forwarding reference because auto is deduced.

auto f = [](auto&& x)
{
    // forwarding reference
};

Quick mental rule

Ask two questions:

  1. Is the type being deduced?
  2. Is the parameter exactly T&& (or auto&&)?

If both are true, it's a forwarding reference.

Otherwise, it's an rvalue reference.

Summary

// Rvalue references
void f(std::string&&);
void f(int&&);

template<typename T>
struct X {
    void g(T&&);          // rvalue reference
};

// Forwarding references
template<typename T>
void f(T&&);

auto lambda = [](auto&& x) { };

// Not forwarding references
template<typename T>
void f(const T&&);

template<typename T>
void f(std::vector<T>&&);

The key distinction is that a forwarding reference is a special kind of && parameter that participates in type deduction and can bind to both lvalues and rvalues, enabling perfect forwarding. An rvalue reference always refers only to rvalues and is primarily used to implement move semantics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment