They wouldn't be.
The comments show that the "extra" references are dropped. That's a good thing, because otherwise the program would be invalid (as there are no "references to references" in the language).
[dcl.ref]/6
: If a typedef-name ([dcl.typedef], [temp.param]) or a decltype-specifier denotes a type TR
that is a reference to a type T
, an attempt to create the type “lvalue reference to cv TR
” creates the type “lvalue reference to T
”, while an attempt to create the type “rvalue reference to cv TR
” creates the type TR
. [ Note: This rule is known as reference collapsing. — end note ] [ Example:
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 = 5; // r5 has the type int&&
decltype(r2)& r6 = i; // r6 has the type int&
decltype(r2)&& r7 = i; // r7 has the type int&
— end example ]
This is a tool to make template programming easier: you don't have to strip the "redundant" references off yourself. Arguably you wouldn't often encounter the need for this, but in more complex codebases with lots of templates and/or aliases, it can happen.
When you have a function template like this:
template <typename T>
void foo(T&& arg)
and pass an lvalue int
, then your arg
will be an int&
; if you pass an rvalue int
, then it'll be an int&&
. This is called a "forwarding reference" (or "universal reference"), and is not the same rule as reference collapsing, though it does have similar utility in making life easier for template programming.