Syntax for universal references

A universal reference such as T&& can deduce T to be an “object type“, or a “reference type

In your example it can deduce T as int when passed an rvalue, so the function parameter is int&&, or it can deduce T as int& when passed an lvalue, in which case the function parameter is int& (because the reference collapsing rules say std::add_rvalue_reference<int&>::type is just int&)

If T isn’t deduced by the function call (as in your X::baz example) then it can’t be deduced to int&, so the reference isn’t a universal reference.

So IMHO there’s really no need for new syntax, it fits nicely into template argument deduction and reference collapsing rules, with the small tweak that a template parameter can be deduced as a reference type (where in C++03 a function template parameter of type T or T& would always deduce T as an object type.)

These semantics and this syntax were proposed right from the beginning when rvalue references and a tweak to the argument deduction rules were proposed as the solution to the forwarding problem, see N1385. Using this syntax to provide perfect forwarding was proposed in parallel with proposing rvalue references for the purposes of move semantics: N1377 was in the same mailing as N1385. I don’t think an alternative syntax was ever seriously proposed.

IMHO an alternative syntax would actually be more confusing anyway. If you had template<typename T> void bar(T&@) as the syntax for a universal reference, but the same semantics as we have today, then when calling bar(i) the template parameter T could be deduced as int& or int and the function parameter would be of type int& or int&& … neither of which is “T&@” (whatever that type is.) So you’d have grammar in the language for a declarator T&@ which is not a type that can ever exist, because it actually always refers to some other type, either int& or int&&.

At least with the syntax we’ve got the type T&& is a real type, and the reference collapsing rules are not specific to function templates using universal references, they’re completely consistent with the rest of the type system outside of templates:

struct A {} a;
typedef A& T;
T&& ref = a;    // T&& == A&

Or equivalently:

struct A {} a;
typedef A& T;
std::add_rvalue_reference<T>::type ref = a;    // type == A&

When T is an lvalue reference type, T&& is too. I don’t think a new syntax is needed, the rules really aren’t that complicated or confusing.

Leave a Comment