Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the standard/official name for universal references?

I know that if a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is widely called a universal reference.

The term universal reference was introduced by Scott Meyers in his original talk “Universal References in C++11”. However, I wonder what's the official/standard term for universal references.

like image 797
101010 Avatar asked Nov 24 '15 21:11

101010


People also ask

What is universal reference?

Universal reference was a term Scott Meyers coined to describe the concept of taking an rvalue reference to a cv-unqualified template parameter, which can then be deduced as either a value or an lvalue reference.

What are rvalue references?

Rvalue references is a small technical extension to the C++ language. Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.

What is forwarding reference in C++?

When t is a forwarding reference (a function argument that is declared as an rvalue reference to a cv-unqualified function template parameter), this overload forwards the argument to another function with the value category it had when passed to the calling function.

What is reference collapsing?

Reference collapsing is the mechanism that leads to universal references (which are really just rvalue references in situations where reference-collapsing takes place) sometimes resolving to lvalue references and sometimes to rvalue references.


1 Answers

Overview

It is known that since C++11, a parameter of type T&& is called an rvalue reference [ISO/IEC 14882:2011 §8.3.2/p2 References [dcl.ref] ]. That is, unless T is a template parameter type or auto or a typedef for some lvalue reference type.

examples:

template<typename T>
void foo(T&& p) { // -> T is a template parameter
  ...
}

auto &&p = expression;

Although technically T&& in the examples above is still an rvalue reference, its behaviour differs significantly from a regular one.

Naturally, you would ask "why this special case doesn't have a special syntax". The answer is that the && syntax was intentionally overloaded for this special construct by the C++ committee. However, they missed to name this special case.

In the absence of a distinct name for this particular construct, Scott Meyers coined the widely known term/name universal references.

The committee however, decided that this name is not proper for a number of reasons. As such, the proposal N4164 made by Herb Sutter, Bjarne Stroustrup and Gabriel Dos Reis proposed to change the name to Forwarding References.

The name Forwarding References had the most support in informal discussions among committee members, including the authors of the proposal mentioned earlier. Interestingly enough, it was Scott Meyers himself that introduced that term in his original “Universal References” talk. However, later he decided to go along with the name universal references. For this decision played role the fact that at the time he didn't think that the term forwarding references included also the auto&& case.

Why not universal references?

According to the proposal the term Universal references although is a reasonable name with an obvious meaning, it happens to be wrong in several aspects.

A universal reference must mean the following:

  • A reference that can be used everywhere; or
  • A reference that can be used for everything; or
  • something similar.

Obviously this is not the case nor is the appropriate use of this construct. Furthermore, this name would encourage many people to consider that something having such a name is meant to be used "universally". Something that the committee considered it a bad thing.

Moreover, "universal references" aren’t even really references per se, but rather a set of rules for using references in a particular way in a particular context with some language support for that use, and that use is forwarding.

Why auto&& is Also Considered a Forwarding case

auto&& is also considered a forward case since it follows the reference collapsing rules. For example in:

  • Generic lambdas of the form, [](auto&& x){ … }
  • for-ranged loop of the form, for(auto &&i : v) { ... }
  • Finally, in general is true that auto&& local variables are for forwarding.

Standard Wordings for Forwarding References

The term forwarding references is mentioned in the draft standard N4527 in the following places:

§14.8.2.1/ Deducing template arguments from a function call [temp.deduct.call] (Emphasis Mine):

If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. A forwarding reference is an rvalue reference to a cv-unqualified template parameter. If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction. [ Example:

template <class T> int f(T&& heisenreference);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
// would bind an rvalue reference to an lvalue

— end example ]

§14.8.2.5/p10 Deducing template arguments from a type [temp.deduct.type]:

Similarly, if P has a form that contains (T), then each parameter type Pi of the respective parameter-typelist of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A. If P and A are function types that originated from deduction when taking the address of a function template (14.8.2.2) or when deducing template arguments from a function declaration (14.8.2.6) and Pi and Ai are parameters of the top-level parameter-type-list of P and A, respectively, Pi is adjusted if it is a forwarding reference (14.8.2.1) and Ai is an lvalue reference, in which case the type of Pi is changed to be the template parameter type (i.e., T&& is changed to simply T). [ Note: As a result, when Pi is T&& and Ai is X&, the adjusted Pi will be T, causing T to be deduced as X&. — end note ] [Example:

template <class T> void f(T&&);
template <> void f(int&) { } // #1
template <> void f(int&&) { } // #2
void g(int i) {
f(i); // calls f<int&>(int&), i.e., #1
f(0); // calls f<int>(int&&), i.e., #2
}

— end example ] If the parameter-declaration corresponding to Pi is a function parameter pack, then the type of its declaratorid is compared with each remaining parameter type in the parameter-type-list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. During partial ordering (14.8.2.4), if Ai was originally a function parameter pack:

like image 148
101010 Avatar answered Oct 19 '22 09:10

101010