Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C++  Module 12 – Move semantics and smart pointers

Forwarding references and perfect forwarding

How T&& in a C++ template binds to any argument, how reference collapsing and std::forward pass each argument on unchanged, and where forwarding breaks.

  • Advanced
  • 20 minutes
  • Examples run with Clang 21.0.0, GCC 16.2.0 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 8 of 20 minutes free
Free: T&& is a forwarding reference only when T is deduced · Reference collapsing gives the parameter its type · std::forward restores the caller's value category · Practise: deduce, collapse, forward. With a pass: 2 in-depth parts and 3 interview answers, about 12 minutes.

What you will learn

  • Recognise a forwarding reference and tell it from an rvalue reference
  • Apply the reference collapsing rules to work out a parameter's type
  • Pass arguments on with std::forward so that lvalues are copied and rvalues moved
  • Debug forwarding mistakes such as std::move on a forwarding reference

Before you start

A logging factory that takes const std::string& copies every name it is given, even a temporary it could have moved, and one that takes std::string&& rejects a named string. A forwarding reference: A reference written auto&&, or T&& in a template whose T is deduced, that binds to anything: it becomes an lvalue reference for a named variable and an rvalue reference for a temporary. More in “C++ auto, decltype and type aliases” accepts both: a parameter written T&&, where T is a template parameter deduced from the call, as in template <typename T> void f(T&& x), or a variable declared auto&&. For an lvalue, T is deduced as an lvalue reference, and reference collapsing: The rule that turns a reference to a reference, formed through a template parameter or an alias, into one reference: an lvalue reference wins whenever one is involved, and only && combined with && stays &&. With T = std::string&, T&& is std::string&. turns T&& into an lvalue reference too; for an rvalue, T is the plain type and the parameter is an rvalue reference: A reference declared with &&, such as int&& r, that binds to temporaries and other objects about to expire but never to a named variable. Unlike a const reference, it lets you change the object it names. More in “C++ references: lifetime extension and dangling”. Inside the function the parameter has a name, so it is an lvalue, and std::forward<T>(x) casts it back to the category the caller used. Doing that for every argument is perfect forwarding: Passing each argument of a wrapper on with the value category the caller gave it, through a forwarding reference and std::forward<T>, so that the wrapped call copies lvalues and moves rvalues exactly as a direct call would.: emplace_back and std::make_unique hand their arguments to a constructor as if the caller had called it directly, copying lvalues and moving rvalues.

T&& is a forwarding reference only when T is deduced

The rule is narrow. The parameter must be exactly T&&, with no const, and T must be deduced from this very call. Forwarding references are the parameters of function templates written like that, and auto&& variables. Everything else spelled with && is an ordinary rvalue reference that accepts only rvalues:

  • const T&& is an rvalue reference to const, because the const breaks the rule.
  • std::vector<T>&& is an rvalue reference to a vector, because the parameter is not T&& itself.
  • void push(T&& item) inside template <typename T> class Queue is an rvalue reference, because T was fixed when the class was instantiated, not deduced by the call.

Step through the tests with each declaration:

Interactive · stage by stage Is this T&& a forwarding reference? Step through five declarations. A declaration that passes every test is a forwarding reference; one that fails a test is an rvalue reference.
Is this T&& a forwarding reference?, step 6 of 6. Showing Written exactly T&&? (const T&&: no), The whole type is T&&? (std::vector<T>&&: no), T deduced by this call? (Queue<T>::push: no), A forwarding reference (f(T&& x): yes). In focus: A forwarding reference.Written exactly T&&?const T&&: noThe whole type is T&&?std::vector<T>&&: noT deduced by this call?Queue<T>::push: noA forwarding referencef(T&& x): yes

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. f(T&& x). template <typename T> void f(T&& x): the parameter is written exactly T&&, T is a template parameter of f, and every call deduces it. It passes all three tests: a forwarding reference.
  2. auto&& x. A variable declared auto&& is deduced from its initialiser in the same way, so it is a forwarding reference too: auto&& x = make(); binds to the temporary, auto&& y = s; to the named s.
  3. const T&&. template <typename T> void f(const T&& x): the const breaks the first test. This is an rvalue reference to const, which accepts only rvalues.
  4. std::vector<T>&&. template <typename T> void f(std::vector<T>&& v): T is deduced, but the parameter is not T&& itself. It is an rvalue reference to a vector, so a named vector does not bind.
  5. Queue<T>::push. void push(T&& item) in template <typename T> class Queue: T was fixed when Queue<T> was instantiated, so no call deduces it. push accepts only rvalues.
  6. What T records. For an lvalue argument, a forwarding reference deduces T as an lvalue reference; for an rvalue, as the plain type. The program below prints what T became for four arguments.

The program below calls template <typename T> void show(T&&) with four arguments and prints what T and the parameter became:

deduce.cpp C++ · deduce.cpp
// What T becomes in template <typename T> void show(T&& x), for four kinds of argument, and what T&& collapses to.
#include <print>
#include <string>
#include <utility>

template <typename T> constexpr const char* name = "?";
template <> constexpr const char* name<std::string> = "std::string";
template <> constexpr const char* name<std::string&> = "std::string&";
template <> constexpr const char* name<std::string&&> = "std::string&&";
template <> constexpr const char* name<const std::string> = "const std::string";
template <> constexpr const char* name<const std::string&> = "const std::string&";
template <> constexpr const char* name<const std::string&&> = "const std::string&&";

template <typename T>
void show(const char* call, T&&) {
    std::println("{:<26} T = {:<20} parameter = {}", call, name<T>, name<T&&>);
}

std::string make() { return "made"; }

int main() {
    std::string s = "text";
    const std::string c = "constant";
    show("show(s)", s);
    show("show(c)", c);
    show("show(std::move(s))", std::move(s));
    show("show(make())", make());
}
What T becomes for four kinds of argument Python · deduce_session.py
from session import run

run("""
clang++ -std=c++23 -Wall -Wextra deduce.cpp -o deduce && ./deduce
g++-16 -std=c++23 -Wall -Wextra deduce.cpp -o deduce-gcc
./deduce > clang.txt && ./deduce-gcc > gcc.txt && diff clang.txt gcc.txt && echo "GCC deduces the same"
""")

Output

$ clang++ -std=c++23 -Wall -Wextra deduce.cpp -o deduce && ./deduce
show(s)                    T = std::string&         parameter = std::string&
show(c)                    T = const std::string&   parameter = const std::string&
show(std::move(s))         T = std::string          parameter = std::string&&
show(make())               T = std::string          parameter = std::string&&
$ g++-16 -std=c++23 -Wall -Wextra deduce.cpp -o deduce-gcc
$ ./deduce > clang.txt && ./deduce-gcc > gcc.txt && diff clang.txt gcc.txt && echo "GCC deduces the same"
GCC deduces the same

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 deduce_session.py

For the lvalue s, T is std::string&, and the const lvalue c keeps its const in T. For the xvalue std::move(s) and the prvalue make(), T is plain std::string, and the parameter is std::string&&. That asymmetry, a reference in T only for lvalues, is how the function can later tell what it was given.

Checkpoint

Check: Recognising a forwarding reference

  1. Question 1 of 2 const std::string c = "constant"; show(c); with template <typename T> void show(T&&). What is T?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: const std::string&

    A forwarding reference records the argument's category in T: a reference for an lvalue, with its const, and the plain type for an rvalue.

  2. Question 2 of 2 Queue<std::string> q; has the member void push(T&& item). Does q.push(s) compile for a named std::string s?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: No, because item is a std::string&&, which cannot bind to the lvalue s

    A class template's member function receives T ready-made, so its T&& is an ordinary rvalue reference. To accept lvalues too, push would need its own template parameter, as in template <typename U> void push(U&& item).

Reference collapsing gives the parameter its type

T&& with T = std::string& would be a reference to a reference, which C++ does not allow you to write. When such a type arises through a template parameter or an alias, the standard collapses it into one reference, and an lvalue reference wins whenever one is involved. In the table, U stands for any type, such as std::string:

T is T& becomes T&& becomes
U& U& U&
U&& U& U&&
U U& U&&

Only an rvalue reference combined with an rvalue reference stays an rvalue reference. The “parameter” column of the run above is this table applied to the deduced T: for show(c), T is const std::string&, so T&& collapses to const std::string&. The same table explains auto&&: in auto&& y = s;, auto is deduced as std::string&, so y is a std::string& bound to s.

Checkpoint

Check: Reference collapsing

  1. Question 1 of 2 With T = const std::string&, what type is T&&?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: const std::string&

    This is the show(c) row of the run: T is const std::string&, and the parameter is const std::string&.

  2. Question 2 of 2 using R = int&&; What type is R&&?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: int&&

    The table's middle row: with T = U&&, T& is U& and T&& is U&&.

std::forward restores the caller’s value category

std::forward<T>(x) is defined as static_cast<T&&>(x). When T is std::string&, that collapses to std::string& and the result is an lvalue; when T is std::string, the result is an xvalue that can be moved from, as the two columns of the diagram show:

For f(name), T is std::string& and std::forward gives an lvalue; for f(std::move(name)), T is std::string and it gives an xvalue.template <typename T>void f(T&& x)lvalue argumentrvalue argumentf(name)f(std::move(name))T is deduced asstd::string&std::stringT&& collapses to the parameter typestd::string&std::string&&std::forward<T>(x) givesan lvalue: copiedan xvalue: movedPassed on by name, x is an lvalue;std::forward restores what was passed.

How a forwarding reference remembers what the caller passed

Text description of the diagram

The diagram is titled "template <typename T> void f(T&& x)" and has two columns, one for an lvalue argument and one for an rvalue argument. Each column has four steps from top to bottom.

  1. The call: f(name) on the left, f(std::move(name)) on the right.
  2. T is deduced as: std::string& on the left, std::string on the right.
  3. T&& collapses to the parameter type: std::string& on the left, std::string&& on the right.
  4. std::forward<T>(x) gives: an lvalue, which is copied, on the left; an xvalue, which is moved, on the right.

A note at the bottom says that x, passed on by name, is an lvalue, and that std::forward restores what was passed.

You always write the template argument, because std::forward must not deduce it: the information lives in T, not in the type of x. A factory that forwards a pack of arguments passes each one on with its own category:

make_logged.cpp C++ · make_logged.cpp
// make_logged<T>(args...) passes its arguments on to T's constructor exactly as it received them, and each
// constructor of Badge prints which one ran.
#include <print>
#include <string>
#include <utility>

struct Badge {
    std::string name;
    int level = 0;
    Badge(const std::string& n) : name(n) { std::println("    Badge(const std::string&): copied the name"); }
    Badge(std::string&& n) : name(std::move(n)) { std::println("    Badge(std::string&&): moved the name"); }
    Badge(const std::string& n, int l) : name(n), level(l) { std::println("    Badge(const std::string&, int)"); }
};

template <typename T, typename... Args>
T make_logged(Args&&... args) {
    std::println("make_logged with {} argument(s)", sizeof...(args));
    return T(std::forward<Args>(args)...);
}

int main() {
    std::string name = "Asha Rao";
    Badge a = make_logged<Badge>(name);              // an lvalue: copied
    Badge b = make_logged<Badge>(std::move(name));   // an xvalue: moved
    Badge c = make_logged<Badge>(std::string("Ravi"));
    Badge d = make_logged<Badge>(a.name, 3);
    std::println("a = {}, b = {}, c = {}, d = {} (level {}); name is now \"{}\"", a.name, b.name, c.name, d.name, d.level, name);
}
Which Badge constructor each call reached Python · make_logged_session.py
from session import run

run("""
clang++ -std=c++23 -Wall -Wextra make_logged.cpp -o make_logged && ./make_logged
g++-16 -std=c++23 -Wall -Wextra make_logged.cpp -o make_logged-gcc
./make_logged > clang.txt && ./make_logged-gcc > gcc.txt && diff clang.txt gcc.txt && echo "GCC prints the same"
""")

Output

$ clang++ -std=c++23 -Wall -Wextra make_logged.cpp -o make_logged && ./make_logged
make_logged with 1 argument(s)
    Badge(const std::string&): copied the name
make_logged with 1 argument(s)
    Badge(std::string&&): moved the name
make_logged with 1 argument(s)
    Badge(std::string&&): moved the name
make_logged with 2 argument(s)
    Badge(const std::string&, int)
a = Asha Rao, b = Asha Rao, c = Ravi, d = Asha Rao (level 3); name is now ""
$ g++-16 -std=c++23 -Wall -Wextra make_logged.cpp -o make_logged-gcc
$ ./make_logged > clang.txt && ./make_logged-gcc > gcc.txt && diff clang.txt gcc.txt && echo "GCC prints the same"
GCC prints the same

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 make_logged_session.py

Step through the four calls:

Interactive · message by message Four calls through make_logged Step through main's four calls. The middle column forwards each argument; the right one shows which constructor it reached.
Four calls through make_logged, step 7 of 7. Messages so far: main to make_logged<Badge>: name, an lvalue; make_logged<Badge> to Badge: forward: an lvalue; main to make_logged<Badge>: std::move(name); make_logged<Badge> to Badge: forward: an xvalue; main to make_logged<Badge>: std::string("Ravi"); main to make_logged<Badge>: a.name, 3; make_logged<Badge> to Badge: forward: both.mainname is now ""make_logged<Badge>Args = std::string&, intBadgeBadge(const string&, int)name, an lvalueforward: an lvaluestd::move(name)forward: an xvaluestd::string("Ravi")a.name, 3forward: both

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. An lvalue. make_logged<Badge>(name) passes the named string. Args is deduced as std::string&, so the parameter is an lvalue reference to name.
  2. It arrives as an lvalue. std::forward<std::string&>(args) gives an lvalue, so Badge(const std::string&) runs and copies the name, as a direct Badge(name) would.
  3. An xvalue. make_logged<Badge>(std::move(name)) passes an xvalue. Args is deduced as plain std::string, so the parameter is std::string&&.
  4. It arrives as an rvalue. std::forward<std::string>(args) gives an xvalue, so Badge(std::string&&) runs and moves the characters out of name, which ends up empty.
  5. A temporary. std::string("Ravi") is a prvalue. It binds to the parameter, which is std::string&& again, and reaches the moving constructor in the same way.
  6. Two arguments. make_logged<Badge>(a.name, 3) forwards a pack of two: an lvalue string and an int. Each keeps its own category, and the two-parameter constructor runs.
  7. The same constructors. Every call reached the constructor a direct call would have reached. The run ended with a = Asha Rao, b = Asha Rao, c = Ravi, d = Asha Rao (level 3) and an empty name.

make_logged<Badge>(name) reached the constructor that copies, and make_logged<Badge>(std::move(name)) the one that moves, so name ended up empty, as it would with Badge(std::move(name)) written directly. A temporary went to the moving constructor, and two arguments went to the two-parameter constructor. Perfect forwarding is exactly that: make_logged adds a log line and changes nothing about which constructor runs. It is also how emplace_back, std::make_unique and std::invoke pass on what they receive. Rule F.19 of the C++ Core Guidelines puts it in one line: pass “forward” parameters as TP&& and only std::forward them.

Key idea

A named parameter is an lvalue whatever the caller passed; std::forward<T> restores the caller’s category from T, so a wrapper reaches the constructor that a direct call would reach.

Where forwarding goes wrong, and where it is used

  • std::move on a forwarding reference moves from the caller’s lvalue too, so the caller’s object is emptied.
  • Forwarding twice passes an rvalue on twice, so the second receiver gets a moved-from object.
  • A greedy forwarding constructor matches a non-const object better than the copy constructor does.
  • An overload next to a forwarding template loses arguments of nearby types, such as a short passed to an int overload, to the template.
  • emplace_back forwards constructor arguments into the vector’s own storage, so no temporary is built first.
  • decltype(auto) makes a call wrapper return exactly the type the wrapped call returns, references included.
  • auto&& in a range-for loop binds to every element type, including the proxy objects of std::vector<bool>.
  • A generic lambda with an auto&& parameter has a forwarding reference too, but no template parameter to name in std::forward<T>.

Checkpoint

Check: std::forward and perfect forwarding

  1. Question 1 of 2 make_logged<Badge>(name) is called with the named string name. Which constructor did it reach?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Badge(const std::string&), which copied the name

    Perfect forwarding reaches the constructor a direct call would reach: the copying one for an lvalue.

  2. Question 2 of 2 After Badge b = make_logged<Badge>(std::move(name));, what did the run print for name, and why?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: An empty string, because std::forward passed the xvalue on and Badge's moving constructor took the characters

    The program ended with name is now "", exactly as Badge(std::move(name)) written directly would leave it.

Practise: deduce, collapse, forward

Questions on deduction, collapsing and std::forward follow, weakest idea first; a missed one returns before the end.

Practice that adapts to you

Practise this lesson

11 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 11 Which of these parameters are forwarding references?

    Choose every answer that is right.

    Show the answer: Question 1 of 11

    Answer:

    • x in auto&& x = make();
    • x in template <typename T> void f(T&& x)

    A forwarding reference is exactly T&& with T deduced by the call, or auto&&. const T&& is an rvalue reference to const, and T in Queue's member function was fixed when the class was instantiated, so push only accepts rvalues.

  2. Question 2 of 11 std::string s; f(s); with template <typename T> void f(T&& x). What is T?

    Choose one answer.

    Show the answer: Question 2 of 11

    Answer: std::string&

    For an lvalue argument T is deduced as an lvalue reference, std::string&. The parameter type T&& then collapses to std::string&, as the lesson's run printed.

  3. Question 3 of 11 std::string s = "text"; auto&& y = s; What is the type of y?

    Choose one answer.

    Show the answer: Question 3 of 11

    Answer: std::string&, bound to s

    auto&& follows the rules of T&&: the initialiser's category decides, and the variable binds to whatever it is given.

  4. Question 4 of 11 A function template has the parameter const T&& x. Which arguments can it take?

    Choose one answer.

    Show the answer: Question 4 of 11

    Answer: Only rvalues, such as a temporary or std::move(s)

    Adding const to T&& turns a forwarding reference into an rvalue reference to const, which refuses named arguments.

  5. Question 5 of 11 With using R = int&&;, what is the type R&?

    Choose one answer.

    Show the answer: Question 5 of 11

    Answer: int&

    Reference collapsing gives an lvalue reference whenever an lvalue reference is involved. Only && combined with && stays an rvalue reference.

  6. Question 6 of 11 template <typename T> void g(T& x); is called as g<std::string&&>(s). What is the parameter type?

    Choose one answer.

    Show the answer: Question 6 of 11

    Answer: std::string&

    The same rule works whether T is deduced or written out: & with anything is &.

  7. Question 7 of 11 How can template <typename T> void f(T&& x) tell, inside, whether its argument was an lvalue?

    Choose one answer.

    Show the answer: Question 7 of 11

    Answer: From T, which is an lvalue reference only for an lvalue argument

    A reference in T for lvalues only, plus collapsing, gives the parameter the right type in both cases and leaves the caller's category in T for std::forward to use.

  8. Question 8 of 11 How is std::forward<T>(x) defined?

    Choose one answer.

    Show the answer: Question 8 of 11

    Answer: As static_cast<T&&>(x)

    std::forward and std::move are both casts; std::forward's result depends on T, std::move's never does.

  9. Question 9 of 11 make_badge(std::string("Ravi")) is called with this template. Which Badge constructor runs?

    Read the code, then choose one answer.

    template <typename T>
    Badge make_badge(T&& name) {
        return Badge(name);
    }
    Show the answer: Question 9 of 11

    Answer: Badge(const std::string&), because the parameter name is an lvalue inside the function

    T is std::string and the parameter is std::string&&, but the expression name is an lvalue, so the copying constructor is chosen. Badge(std::forward<T>(name)) would reach the moving one.

  10. Question 10 of 11 Why must you write the template argument in std::forward<T>(x)?

    Choose one answer.

    Show the answer: Question 10 of 11

    Answer: Because the category of the original argument is recorded only in T; x itself is always an lvalue

    Inside the function x has a name and is an lvalue whatever was passed. The only record of an rvalue argument is that T was deduced as a plain type instead of a reference, so std::forward needs T to cast back correctly.

  11. Question 11 of 11 emplace_back forwards its arguments to the element's constructor. What does v.emplace_back(name) do with a named std::string name, for a std::vector<std::string> v?

    Choose one answer.

    Show the answer: Question 11 of 11

    Answer: Copies name into the new element, because an lvalue is forwarded as an lvalue

    emplace_back is perfect forwarding at work: copies for lvalues, moves for rvalues, each element built where it is stored.

Four forwarding bugs reproduced, with their fixes Pro: opens with a Pro, Premium or Ultimate pass

Forwarding mistakes rarely draw a warning, and their symptoms appear far from the template that caused them. Each of the four below is reproduced on Apple clang 21 and GCC 16, with the change that fixes it.

In this part:

  • std::move on a forwarding reference empties the caller’s object
  • Forwarding the same argument twice
  • A forwarding constructor that beats the copy constructor
  • An overload that a forwarding template outbids

8 runnable examples (C++, Python) · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Forwarding in real code: emplace, call wrappers and auto&& Pro: opens with a Pro, Premium or Ultimate pass

Forwarding is at work in code that most C++ programs use daily: emplace_back, call wrappers, range-for loops and generic lambdas. Each is run below on both compilers, and an exercise builds a factory and a call wrapper that must keep every category intact.

In this part:

  • Building in place against pushing a temporary, counted
  • A call wrapper that keeps reference returns
  • auto&& in a range-for loop and proxy references
  • Forwarding from a generic lambda
  • Your turn: a factory and a call wrapper

10 runnable examples (C header, C++, Python) · about 4 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Interview questions

  1. Fresher to mid · Warm-up

    Is every T&& in a template a forwarding reference?

    No. T&& is a forwarding reference only when T is a template parameter of the function itself, deduced from the call, and the parameter is written exactly T&&, without const; auto&& variables are forwarding references too. const T&& is an rvalue reference to const, std::vector<T>&& is an rvalue reference to a vector, and T&& in a member function of a class template, such as void push(T&& item) in template <typename T> class Queue, is an ordinary rvalue reference, because T was fixed when the class was instantiated. Only a forwarding reference binds to lvalues as well as rvalues, deducing T as an lvalue reference for an lvalue, and only for it is std::forward<T> the right way to pass the parameter on.

  2. Mid to senior

    Widget has a constructor template, template <typename T> Widget(T&& name), to accept anything a std::string can be made from. Copying a const Widget works, but Widget b(a); for a non-const a fails to compile with an error about building a std::string from a Widget. Explain why, and fix it two ways.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Fresher to mid

    A task queue has template <typename Task> void enqueue(Task&& t) { tasks_.push_back(std::move(t)); }. After a refactor, callers find their own task objects empty after enqueue(task). Explain the bug, show the fix, and say when std::move on a parameter like t would be right.

    Model answer, Pro. First step and follow-ups

  4. Senior to staff

    Write timed(f, args...), which calls f with args, logs how long the call took and returns f's result unchanged, including references and void. What must the implementation get right, and how would you test it?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • T&& with T deduced by the call, and auto&&, are forwarding references: they bind to lvalues and rvalues alike.
  • For an lvalue argument T is an lvalue reference; reference collapsing turns T&& into an lvalue reference.
  • A parameter passed on by name is an lvalue; std::forward<T>(x) restores the category the caller passed.
  • Perfect forwarding passes every argument on unchanged, so a wrapper reaches the same constructor as a direct call.
  • const T&&, std::vector<T>&& and T&& in a class template’s member function are ordinary rvalue references.

Terms in this lesson

perfect forwarding
Passing each argument of a wrapper on with the value category the caller gave it, through a forwarding reference and std::forward<T>, so that the wrapped call copies lvalues and moves rvalues exactly as a direct call would.
reference collapsing
The rule that turns a reference to a reference, formed through a template parameter or an alias, into one reference: an lvalue reference wins whenever one is involved, and only && combined with && stays &&. With T = std::string&, T&& is std::string&.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.