When ambiguity arises, partial ordering of templates is used to resolve it. However, this partial ordering is established on the templates as they are before any substitution happens, and not after (partial or complete) substitution has been performed - which is what you are expecting by replacing int
for T
in typename g<T>::type
, which yields typename g<int>::type
and therefore (because of the definition of g
) void
.
Paragraph 14.8.2.4/2 specifies how partial ordering is established (see this answer on SO for a more detailed discussion of the paragraph below):
Two sets of types are used to determine the partial ordering. For each of the templates involved there is
the original function type and the transformed function type. [ Note: The creation of the transformed type
is described in 14.5.6.2. —end note ] The deduction process uses the transformed type as the argument
template and the original type of the other template as the parameter template. This process is done twice
for each type involved in the partial ordering comparison: once using the transformed template-1 as the
argument template and template-2 as the parameter template and again using the transformed template-2
as the argument template and template-1 as the parameter template.
Before any substitution, without knowing what value T
will assume, you cannot tell (and neither the compiler can tell) whether case B is more or less specialized than case A. Therefore, neither of the two specializations is more specialized than the other.
In other words, the question isn't whether this partial specialization:
template<class T> struct f<T, void>; // Case A
Is more specialized than this one (obtained through partial substitution):
template<class T> struct f<T*, void>; // Case B
If that were what you have, the answer would be obviously that case B is more specialized. Instead, the question is whether for any possible T
, this specialization:
template<class T> struct f<T, void>; // Case A
Is more specialized than this one:
template<class T> struct f<T*, typename g<T>::type>; // Case B
Since that cannot be established for any T
, case B is neither more specialized nor less specialized than case A, and when both are viable, you get an ambiguity.
If you are wondering whether parameters in a non-deduced context are taken into consideration for partial ordering, this is mentioned in a note to Paragraph 14.8.2.4/11:
In most cases, all template parameters must have values in order for deduction to succeed, but for partial
ordering purposes a template parameter may remain without a value provided it is not used in the types
being used for partial ordering. [ Note: A template parameter used in a non-deduced context is considered
used. —end note ]