Class template specialization partial ordering and function synthesis

Barry :

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?

Richard Smith :

Clang is being GCC-compatible (and compatible with existing code that depends on both of these behaviors).

Consider [temp.deduct.type]p1:

[...] an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.

The crux of the issue is what "compatible" means here.

When partially ordering function templates, Clang merely deduces in both directions; if deduction succeeds in one direction but not the other, it assumes that means the result will be "compatible", and uses that as the ordering result.

When partially ordering class template partial specializations, however, Clang interprets "compatible" as meaning "the same". Therefore it only considers one partial specialization to be more specialized than another if substituting the deduced arguments from one of them into the other would reproduce the original partial specialization.

Changing either of these two to match the other breaks substantial amounts of real code. :(

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

`template <auto>` and partial class template specialization ordering

Partial specialization of a class template member function

Partial template specialization for template class like std::function

Class template partial specialization equivalence

Template class partial specialization syntax

explicit specialization of a function template (which is a member of a class template) produces "partial specialization is not allowed" error, why?

Partial specialization of nested template template class

partial template specialization for template pointer to function

Template function specialization for template class

something confusing about class template partial specialization & class member specialization

Partial class template specialization c++11

Class template argument deduction with partial specialization

Is noexcept deduction allowed in class template partial specialization?

Partial specialization or instantiation of template class method

Partial Template specialization definition outside of class definition

Clang partial class template specialization error

Parameterization and "function template partial specialization is not allowed"

Partial specialization of variadic template member function

Partial template specialization only for a single function

How to work around partial specialization of function template?

Partial specialization rejection with function as template argument

Specialization of member function of class template

template class - member function specialization

Class Template Specialization for one Function

Specialization template class with templated function

Template member function specialization in a template class

Template specialization base class function template missing

Variadic template function specialization in a template class

Variadic template partial specialization of a class to restrict type of template arguments