Consider these cases:
int i{};
int& ir{i};
class A{
public:
int& i;
A(int&& pi):i(pi){}
};
A a1{i}; // Error // case 1
A a2{int(1)}; // OK // case 2
class B{
public:
int& i;
template<typename TYPE>
B(TYPE&& pi):i(pi){}
};
B b1{i}; // OK // case 3
B b2{int(1)}; //OK // case 4
int& ii{int(12)}; // Error // case 5
int& iii{std::move(int(12))}; // Error // case 6
template<typename TYPE>
class C{
public:
TYPE& i;
C(TYPE&& pi):i(pi){}
};
C c1{i}; // Error // case 7
C c2{int(1)}; // OK // case 8
C<int&> c3{i}; // OK // case 9
C<int&> c4{int(1)}; // Error // case 10
int&& iiii{ir}; // Error // case 11
A rvalue can not be bound to lvalue and if my understanding is correct, TYPE&&
would either collapse to TYPE&&
or TYPE&
.
How ever I am having a hard time understanding these cases, specially case 4. If case 4 is correct, then it means we have b2.i
which is a reference, initialized from a temporary (rvalue). Then why case 2, 5, 6 and 7 are incorrect? And when case 9 is correct it mean the TYPE&&
is int&&&
which (I assume) collapses to int&
, then how could c3.i
which is an rvalue(int&&
) be initialized from a lvalue, while case 10 and 11 are incorrect?
I wish some one could explain the general rules regarding this subject and also these cases in detail.
There are several mechanisms at play here: lvalue/rvalue semantics, reference collapsing, forwarding reference and CTAD. I think explaining the cases you listed should be sufficient to form a big picture.
int&& pi
can't be bound to lvaluei
.int&& pi
can be bound to rvalueint(1)
.pi
ini(pi)
refers to a memory location so it is an lvalue, allowinga2.i
to be initialized. After constructiona2.i
is a dangling reference toint(1)
.TYPE&& pi
can be bound to lvaluei
.TYPE
isint&
.b1.i
refers toi
.TYPE&& pi
can be bound to rvalueint(1)
.TYPE
isint
.TYPE&& pi
is an rvalue reference, similar to case 2b2.i
is a dangling reference toint(1)
.int& ii
can not be bound to rvalueint(12)
.int& iii
can not be bound to rvaluestd::move(int(12))
.In cases 7-10
TYPE&& pi
is not a forwarding reference becauseTYPE
is a template parameter ofclass C
, not of constructor. CTAD, used in 7-8 to deduceTYPE
, doesn't change that: an rvalue is expected, only thenTYPE
can be deduced toint
, formingint&& pi
.TYPE
can't be deduced.TYPE
is deduced to beint
.c2.i
, declared asint& i;
, forms a dangling reference toint(1)
.TYPE
is explicitlyint&
.TYPE&& pi
is collapsed toint& pi
.TYPE& i
is collapsed toint& i
.c3.i
refers toi
.TYPE&& pi
is collapsed toint& pi
, lvalue reference can't be bound toint(1)
.int&& iiii
can't be bound to lvalueir
.And a bonus case that might make cases 2 and 4 easier to understand:
The first line is correct, the last is an error.
int&& rvr
is an rvalue reference and extends lifetime of a temporary. Butrvr
mentioned in the last line is an lvalue, so rvalue referenceint&& rvr2
can't be bound to it.