Let's say I have this function:
bool f(int&& one, int&& two) { }
If I attempt to call it with this code:
int x = 4;
f(x, 5);
the compiler will complain that it cannot convert x from lvalue reference to rvalue reference, which is correct.
Now if I convert f into a template function like this:
template <class T, class U>
bool f(T&& one, U&& two) { }
then I can call it with an lvalue reference:
int x = 5;
f(x, 5);
Why is it so? Why doesn't the compiler complain in this case?
Per § 8.3.3 / 6. It's the reference collapsing rule.
template <class T> void func(T&&) // Accepts rvalue or lvalue
void func(T&&) // Accepts rvalue only
void func(T&) // Accepts lvalue only
Worth example from standard draft:
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&
Because there is a template argument deduction, reference collapsing happens. It is what Scott Meyers calls universal references. The U&&
will actually become int &
. There is a nice article and video about how it works and how it could be used.
This happens because of reference collapsing rules added in c++11
A& & becomes A&
A& && becomes A&
A&& & becomes A&
A&& && becomes A&&
In templates these rules are applied but not in a normal function there is no reference collapsing normally in function. There are some other specific situations where reference collapsing will occur like in the presence of auto
, decltype
or a typedef
(that includes using
declarations) That explains the results of your compilation. Reference collapsing had to be added in c++11 because otherwise using references like A & & would become errors since you cannot have a reference to a reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With