abraham_hilbert - 4 months ago 23

C++ Question

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 when an r-value reference remains as an r-value reference and an l-value reference as 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?

Answer

`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&&`

.