Universal vs Forwarding References in C++

When talking about T&& in C++, you may have heard about universal references and forwarding references. This may get you wonder. Why there are two names for an apparently same concept? Is there any difference between them? Which one should I use? Let’s find out.

type&&

Since C++11, two successive ampersands no longer mean just logical and but they are also used to denote so-called rvalue references. However, as many C++ programmers soon find out, not all occurrences of type&& are the same. For example, consider the following code:

void foo(int&& i);

template <typename T>
void bar(T&& i);

When you try to call foo(), it works as expected:

foo(1);      // OK, 1 is an rvalue

int k = 0;
foo(k);      // error: cannot bind 'int' lvalue to 'int&&'

However, for bar(), there is a twist:

bar(1);      // OK

int k = 0;
bar(k);      // OK (?!)

The reason is that when you are in a template and a parameter has exactly type T&& for some deduced type T, then what you might get when instantiating the template is not an rvalue reference. Indeed, in the above case, the parameter of bar() binds to both lvalues and rvalues. On a side note, auto&&works similarly.

Universal References

Since the C++11 standard (or even C++14 for that matter) does not distinguish between true rvalue references and what looks like an rvalue reference but might end up being an lvalue reference, Scott Meyers rightfully decided that we need a name for the latter. And so he coined the term universal references. For example, in the code above, the int&& in the declaration of foo() is an rvalue reference but the T&& in the bar() declaration is a universal reference. The sense behind the name is that a universal reference binds to both lvalues and rvalues, unlike lvalue references to non-const objects (they bind only to modifiable lvalues) and rvalue references (they bind only to rvalues). See Scott’s article for more details.

And so programmers started to use the term, as you can clearly see when you try to google it.

Forwarding References

Later, several members of the C++ standard committee acknowledged the fact that we need a name for the T&& references. However, they decided that a universal reference is not an ideal name, and came up with the term forwarding references in the form of a proposal for the C++1z standard.

I recommend you to read the proposal. It is short (basically three pages), well-written, and answers several questions, including Why not universal reference? Do we really need a name? and What about auto&&?

So, Is There Any Difference Between the Two Terms?

No. They are just different names for the same concept.

Which One Should I Use?

As of C++1z, the name forwarding reference should be part of the standard. For this reason, I recommend you to use it. However, bear in mind that the name universal reference has been with us for quite a while and is used in many articles.

What about you? Do you like the new name, or would you rather keep using the original one?

Discussion

Apart from comments below, you can also discuss this post at /r/cpp and Hacker News.

4 Comments

    • Perfect forwarding refers to the problem of passing arguments from one function (wrapper) to another (wrapped function). To achieve this, one uses forwarding references. Therefore, these two terms are related, but are not the same. Indeed, the first one represents a problem and the second one is a tool to solve the problem. For more details, I recommend reading this great article.

      Reply

Leave a Comment.