Правила выбора специализированной специализации шаблонов предпочтительнее включают переписывание специализаций в шаблоны функций и определение того, какой шаблон функции более специализирован с помощью правил упорядочения для шаблонов функций [temp.class.order]. Рассмотрим этот пример:
#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>{});
}
Оба gcc и clang печатают 2
здесь. Это имеет смысл с некоторыми предыдущими примерами - вывод из контекста без вывода (void
против void_t<T>
) просто игнорируется, поэтому вывод <T, void_t<T>>
в соответствие с <X*, void>
завершается успешно, но вывод <T*, void>
в отношении <Y, void_t<Y>>
не выполняется в обоих аргументы. Хорошо.
Теперь рассмотрим это обобщение:
#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';
}
Оба clang и gcc сообщают об этой специализации как неоднозначную, между 1
и 2
. Но почему? Синтезированные шаблоны функций не являются двусмысленными. Какая разница между этими двумя случаями?