Partial ordering of function templates containing template parameter packs is independent of the number of deduced arguments for those template parameter packs.
template<class...> struct Tuple { };
template< class... Types> void g(Tuple<Types ...>); // #1
template<class T1, class... Types> void g(Tuple<T1, Types ...>); // #2
template<class T1, class... Types> void g(Tuple<T1, Types& ...>); // #3
g(Tuple<>()); // calls #1
g(Tuple<int, float>()); // calls #2
g(Tuple<int, float&>()); // calls #3
g(Tuple<int>()); // calls #3
The above is quoted from partial ordering of overload function templates. I don't quite understand why g(Tuple<int>()); // calls #3
. Specifically, why can't #2
be called? The following are my reasoning, please point out any mistakes :
Note: I will ignore #1
b/c it is explained well here
Step 1: Deduction and substitution and overload resolution come up with these:
- void(Tuple< int>) [T1 = int, Types is empty] #2
- void(Tuple< int>) [T1 = int, Types& is empty] #3
Step 2: Transform both function templates:
- void g(Tuple< C1, Pack1...>);
- void g(Tuple< C2, Pack2&...>);
Step3: This is a function call context, the types are those function parameter types for which the function call has arguments:
- Deduce Tuple< T1, Types...> from Tuple< C2, Pack&...> OK [T1 = C2; Types... = Pack&...]
- Deduce Tuple< T1, Types&...> from Tuple< C1, Pack1...>) OK? [T1 = C1; what about Types&...? Can it be deduced from Pack1...? Are reference dropped here?]
3) If P is a reference type, the type referred to by P is used for deduction.This seems fine.
If P has one of the forms that include a template parameter list < T> or < I>, then each element Pi of that template argument list is matched against the corresponding template argument Ai of its A. If the last Pi is a pack expansion, then its pattern is compared against each remaining argument in the template argument list of A. A trailing parameter pack that is not otherwise deduced, is deduced to an empty parameter pack.
Step4:If the last step is correct. Then it means #3 is not more specialized than #2. So it is ambiguous as to which function template should be resolved to.
Update: I think I misunderstood the relevant quotes above. When we match up template parameters in P with template arguments in A, they are matched up verbatim, which means that all the transforms and analysis done on function call parameters and arguments do not apply again when we match up template parameters/arguments in P/A(actually function call parameter/arguments). Then it will fail in Step 3 above to deduce Tuple< T1, Types&...>
from Tuple< C1, Pack1...>)
. So #3
is more specialized.