Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous template with SFINAE dummy parameter

Consider a case where one needs to verify a type T with another template g (could be some enable_if expression, for example) inside a dummy parameter of another template, like this:

template<class>        struct g { typedef void type; };
template<class, class> struct f {};
template<class T>      struct f<T, void> {};                  // Case A
template<class T>      struct f<T*, typename g<T>::type> {};  // Case B

int main() { f<int*, void> test; }

Here, for the sake of simplicity g doesn't really do anything. The second parameter in Case B is in a nondeduced context, therefore intuitively one would think that Case B is more specialized than Case A. Sadly, both gcc and clang will complain that the template is ambiguous in the instantiation above.

If the dummy parameter were to be removed, then it compiles just fine. How does the addition of a nondeduced parameter somehow destroy the reasonable expectation that T* is more specialized than T?

Here's a quick check using the substitution algorithm:

   f<Q , void      >
-> f<T*, g<Q>::type> // [failed]

   f<Q*, g<Q>::type>
-> f<T , void      > // [to fail or not to fail?]
// One would assume that 2nd parameter is ignored, but guess not?
like image 682
Rufflewind Avatar asked Jul 01 '13 20:07

Rufflewind


1 Answers

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 ]

like image 168
Andy Prowl Avatar answered Sep 29 '22 05:09

Andy Prowl