Remove_reference is defined as follows:
template< class T > struct remove_reference {typedef T type;};
template< class T > struct remove_reference<T&> {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};
When for a type T, remove_reference is instantiated, what is the algorithm for choosing the proper specialization of the template? I don't think this is template argument deduction. Other things that I found were even less relevant (ADL, overload resolution, etc.)
We:
Pick the specializations that match the template arguments. If there is only one, you pick that one. If there are none, you use the primary. Else:
Synthesize a function for each specialization and use the function template partial ordering rules.
In this case, we never have to get to step two. There are three types of types that are relevant: non-references, lvalue references, and rvalue references. So we can make a table:
+---------------------------+-------------+--------------+
| | Matches T&? | Matches T&&? |
+---------------------------+-------------+--------------+
| remove_reference<int> | No | No |
| remove_reference<char&> | Yes, T=char | No |
| remove_reference<float&&> | No | Yes, T=float |
+---------------------------+-------------+--------------+
Note that remove_reference<char&>
does not match remove_reference<T&&>
. The T&&
here is simply rvalue reference to T
, it is not a forwarding reference - a forwarding reference is only a function template argument whose type is an rvalue reference to a template parameter. This is a class template parameter. Even though conceptually with T=char&
, T&&
would be char&
with reference collapsing rules, that will not happen.
In each of the three cases, we either match zero of the specializations - in which we use the primary template - or exactly one of the specializations - in which case we use that particular specialization.