Can someone help me to understand why the output of the following code
template< typename T >
void check()
{
std::cout << "unknow type" << std::endl;
}
template<>
void check<int>()
{
std::cout << "int" << std::endl;
}
template<>
void check<int&>()
{
std::cout << "int&" << std::endl;
}
template<>
void check<int&&>()
{
std::cout << "int&&" << std::endl;
}
template< typename T >
void bar( T&& a )
{
check<T>();
}
int main()
{
bar(0);
int a = 0;
bar( a );
}
is
int
int&
and not
int&&
int&
From my point of view, it seems more intuitive that an r-value reference remains as an r-value reference and an l-value reference an l-value reference, However, it seems that only l-value references remains as l-value references and r-values become non-reference values. What is the motivation/idea behind this?
bar(0);
calls the specialization bar<int>(int&&)
i.e. T
is deduced as int
, so check<T>()
is check<int>()
. The parameter type is T&&
which is int&&
, but that's the type of the parameter, not the type T
.
This is entirely consistent with non-forwarding references. If you define:
template<typename T> void baz(T&);
and you call it with an lvalue of type int
then T
is deduced as int
, not int&
The only thing that's special about forwarding references like your example uses is that for T&&
the type can be deduced as an lvalue reference, call it R
, in which case the parameter type is R&&
which is the same as add_rvalue_reference_t<R>
which is just R
. So for the call bar(a)
you call the specialization bar<int&>(int&)
i.e. T
is deduced as int&
When you call bar<int&&>(0)
with an explicit template argument list there is no argument deduction, and so T
is substituted by int&&
, so the parameter type T&&
is add_rvalue_reference_t<int&&>
which is just int&&
.