Рассмотрим следующие простые (в той мере, в какой это могут быть вопросы шаблона):
#include <iostream>
template <typename T>
struct identity;
template <>
struct identity<int> {
using type = int;
};
template<typename T> void bar(T, T ) { std::cout << "a\n"; }
template<typename T> void bar(T, typename identity<T>::type) { std::cout << "b\n"; }
int main ()
{
bar(0, 0);
}
Оба clang и gcc печатают "a". Согласно правилам [temp.deduct.partial] и [temp.func.order], для определения частичного упорядочения нам нужно синтезировать некоторые уникальные типы. Итак, у нас есть две попытки вычета:
+---+-------------------------------+-------------------------------------------+
| | Parameters | Arguments |
+---+-------------------------------+-------------------------------------------+
| a | T, typename identity<T>::type | UniqueA, UniqueA |
| b | T, T | UniqueB, typename identity<UniqueB>::type |
+---+-------------------------------+-------------------------------------------+
Для вывода на "b", согласно ответа Ричарда Кордена, выражение typename identity<UniqueB>::type
рассматривается как тип и не оценивается. То есть, это будет синтезировано так:
+---+-------------------------------+--------------------+
| | Parameters | Arguments |
+---+-------------------------------+--------------------+
| a | T, typename identity<T>::type | UniqueA, UniqueA |
| b | T, T | UniqueB, UniqueB_2 |
+---+-------------------------------+--------------------+
Ясно, что вывод на "b" терпит неудачу. Это два разных типа, поэтому вы не можете вывести T
для обоих из них.
Однако мне кажется, что вывод на A
должен завершиться неудачей. Для первого аргумента вы должны соответствовать T == UniqueA
. Второй аргумент - это не выведенный контекст - так что не удалось бы сделать вывод, если if UniqueA
были преобразованы в identity<UniqueA>::type
? Последнее является неудачей замещения, поэтому я не вижу, как это удержание может быть успешным.
Как и почему gcc и clang предпочитают "a" перегрузку в этом сценарии?