Частичное упорядочение с использованием шаблона функции с неконтролируемым контекстом

Читая другой вопрос, я столкнулся с проблемой частичного упорядочения, которую я сократил до следующего контрольного примера.

template<typename T>
struct Const { typedef void type; };

template<typename T>
void f(T, typename Const<T>::type*) { cout << "Const"; } // T1

template<typename T>
void f(T, void*) { cout << "void*"; } // T2

int main() {
  // GCC chokes on f(0, 0) (not being able to match against T1)
  void *p = 0;
  f(0, p);
}

Для обоих шаблонов функций тип функции специализации, которая вводит разрешение перегрузки:void(int, void*), Но частичное упорядочение (согласно Comeau и GCC) теперь говорит о том, что второй шаблон более специализирован. Но почему?

Позвольте мне пройти частичное упорядочение и показать, где у меня есть вопросы. майQ быть уникальным составным типом, используемым для определения частичного заказа в соответствии с14.5.5.2.

Transformed parameter-list for T1 (Q inserted): (Q, typename Const<Q>::type*). The types of the arguments are AT = (Q, void*) Transformed parameter-list for T2 (Q inserted): BT = (Q, void*), which are also the types of the arguments. Non-transformed parameter-list for T1: (T, typename Const<T>::type*) Non-transformed parameter-list for T2: (T, void*)

Так как C ++ 03 недооценивает это, я использовал намерение, о котором читал в нескольких отчетах о дефектах. Вышеупомянутый преобразованный список параметров дляT1 (называетсяAT мной) используется в качестве списка аргументов для14.8.2.1 "Deducing template arguments from a function call".

14.8.2.1 не нужно преобразовыватьAT или жеBT сам по себе (например, удаление объявлений объявлений и т. д.), и идет прямо к14.8.2.4, который независимо для каждогоA / P пара делает вывод типа:

AT against T2: { (Q, T), (void*, void*) }. T is the only template parameter here, and it will find that T must be Q. Type deduction succeeds trivially for AT against T2.

BT against T1: { (Q, T), (void*, typename Const<T>::type*) }. It will find that T is Q, too here. typename Const<T>::type* is an un-deduced context, and so it won't be used to deduce anything.

Вот мой первый вопрос: будет ли это теперь использовать значениеT выводить по первому параметру? Если ответ «нет», то первый шаблон более специализирован. Это не может иметь место, потому что и GCC, и Comeau говорят, что второй шаблон более специализирован, и я не верю, что они ошибаются. Итак, мы предполагаем «да» и вставляемvoid* вT, Абзац (14.8.2.4) говорит"Deduction is done independently for each pair and the results are then combined" а также"In certain contexts, however, the value does not participate in type deduction, but instead uses the values of template arguments that were either deduced elsewhere or explicitly specified." Это звучит как "да" тоже.

Поэтому вычет также успешен для каждой пары A / P. Теперь каждый шаблон, по меньшей мере, столь же специализирован, как и другой, потому что дедукция также не зависит от каких-либо неявных преобразований и имеет успех в обоих направлениях. В результате звонок должен быть неоднозначным.

Итак, мой второй вопрос: теперь, почему реализации говорят, что второй шаблон более специализирован? Какой момент я упустил?

Edit: Я проверил явную специализацию и создание экземпляров, и оба, в последних версиях GCC (4.4) скажите мне, что ссылка на специализацию неоднозначна, в то время как более старая версия GCC (4.1) не увеличивает эту ошибку неоднозначности. Это говорит о том, что последние версии GCC имеют непоследовательное частичное упорядочение для шаблонов функций.

template<typename T>
struct Const { typedef void type; };

template<typename T>
void f(T, typename Const<T>::type*) { cout << "Const"; } // T1

template<typename T>
void f(T, void*) { cout << "void*"; } // T2

template<> void f(int, void*) { }
  // main.cpp:11: error: ambiguous template specialization 
  // 'f<>' for 'void f(int, void*)'

Ответы на вопрос(4)

Ваш ответ на вопрос