Home:Professional:Universal References

I recently had occasion to view Scott Meyers' presentation on MSDN Channel 9 about ‘universal references’; he also has a written version of his talk Standard C++ Foundation Web site. The ‘universal reference’ is not a concept you will see defined in the C++ standard, nor is it even something that has any conceptually objective existence in the language or compilation process. It is a construct defined by Meyers in an attempt to make some sense of behavior in the language that he presents as being unexpected or even mysterious. On closer inspection, however, I find that the observed mysterious behavior is actually quite readily explained and has an existing analog that corresponds to already intuitively-understood behavior.

The Mystery

“T&& Doesn't Always Mean ‘Rvalue Reference’”

T&&

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

f()

f(10);

T&&

int i = 11; f(i);

T&

f(int&)

f(int&&)

However, given

template<typename T> void g(std::vector<T> &¶m);

Note that the ‘mystery’ pertains to the type of the argument; it has nothing to do with the fact that inside the function, reference arguments are always lvalue references (because they are named).

Universal References

If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.

T

T

std::vector<T>

std::vector<T>

std::vector<int>

The properties of the ‘universal reference’ are such that it becomes the kind of reference that it is initialized with. Given the declaration of the function template above, where T is considered to be a ‘deduced type’, T&& is a ‘universal reference’. Therefore, in f(10) , the type of the function argument is the rvalue reference int&& because 10 is an rvalue. Similarly, in f(i) the argument type is the lvalue reference int& because i is.

The Truth

int i; typedef int& LRI; typedef int&& RRI; LRI& r1 = i; // r1 has the type int& const LRI& r2 = i; // r2 has the type int& const LRI&& r3 = i; // r3 has the type int& RRI& r4 = i; // r4 has the type int& RRI&& r5 = i; // r5 has the type int&&

typedef int &LRI

RRI &r4

typedef int &&RRI; RRI &&r5

The same thing happens in template parameters, so now it becomes clear what is going on in the template function example:

template<typename T> void f( T &&t ) { printf("in f<>() with %d

", static_cast<int>(t)); } template<> void f<int>( int &&x ) { printf("in f<int>(%d)

", x); } template<> void f<int&>( int &x ) { printf("in f<int&>(%d)

", x); } int main( int argc, char *argv[] ) { // is f<int> because T=int and int && = int&& f(10); // is f<int&> because T=int& so int& && = int& int i = 11; f(i); }

in f<int>(10) in f<int&>(11)

f<int&>

T = int&

T&&

int&

This is why function templates with arguments like g(std::vector<T>&&) won't work: the compiler has no ability through modifying T to collapse the rvalue reference type into an lvalue reference type; and an lvalue actual parameter won't bind to an rvalue reference type.

This is Nothing New

const

typedef const int CI; typedef int NCI; CI r1 = 0; // r1 has the type 'const int' const CI r2 = 0; // r2 has the type 'const int' const NCI r4 = 0; // r4 has the type 'const int' // ERROR: cannot assign to const variable // r4 = 1; NCI r5 = 0; // r5 has the type non-const 'int' r5 = 1;

const

const

const

typedef int NCI; NCI r5 = 0;”

const

To complete the analogy, in

template<typename T> void f(T t);

T

struct X { int i; X(int ii) : i(ii) {} operator int() const { return i; } }; template<typename T> void f(T &t) { printf("in f<>()

"); } template<> void f<X>(X&) { printf("in f<X>(X&)

"); } template<> void f<const X>(const X&) { printf("in f<const X>(const X&)

"); } int main( int argc, char *argv[] ) { X x(1); f(x); // is f<X> const X xc(2); f(xc); // is f<const X> return 0; }

in f<X>(X&) in f<const X>(const X&)

T

f(xc)

T

const X

It Really Wasn't That Complicated

T

const

f()

The mistake is to read much meaning into a template argument type before the template has been instantiated.

Summary

template<typename T> void f(T &¶m);

Posted on 2013/04/06