The rules for picking which class template specialization is preferred involve rewriting the specializations into function templates and determining which function template is more specialized via the ordering rules for function templates [temp.class.order]. Consider this example, then:
#include <iostream>
template <class T> struct voider { using type = void; };
template <class T> using void_t = typename voider<T>::type;
template <class T, class U> struct A { };
template <class T> int foo(A<T, void_t<T>> ) { return 1; }
template <class T> int foo(A<T*, void> ) { return 2; }
int main() {
std::cout << foo(A<int*, void>{});
}
Both gcc and clang print 2
here. This makes sense with some previous examples - deducing against a non-deduced context (void
against void_t<T>
) is just ignored, so deducing <T, void_t<T>>
against <X*, void>
succeeds but deducing <T*, void>
against <Y, void_t<Y>>
fails in both arguments. Fine.
Now consider this generalization:
#include <iostream>
template <class T> struct voider { using type = void; };
template <class T> using void_t = typename voider<T>::type;
template <int I> struct int_ { static constexpr int value = I; };
template <class T, class U> struct A : int_<0> { };
template <class T> struct A<T, void_t<T>> : int_<1> { };
template <class T> struct A<T*, void> : int_<2> { };
int main() {
std::cout << A<int*, void>::value << '\n';
}
Both clang and gcc report this specialization as ambiguous, between 1
and 2
. But why? The synthesized function templates aren't ambiguous. What's the difference between these two cases?
template <class T> void f(A<T, void_t<T>>)
andtemplate <class T> void f(A<T*, void>)
and second should be picked just like in your first example – mpiatekvoid_t
is CWG 2173. – T.C.voider
specialization to the last example in your answer, addtemplate<> struct voider<T0_> { using type = U0_; };
. This will change the result in your example, but not in the algorithm that the compiler actually uses for partial ordering. To test the compilers' algorithm, you could use an example like this one; I've included some explanations in code comments. – bogdan