Все компиляторы, с которыми я мог справиться, согласны, что это нормально:
template <typename Check, typename... T>
auto foo(Check, T...) -> void;
template <typename... T>
auto foo(int, T...) -> void;
int main()
{
foo(7, "");
}
Однако следующий код (с ведущим параметром шаблона, который не может быть выведен из параметров функции) является неоднозначным в соответствии с gcc:
template <typename X, typename Check, typename... T>
auto bar(Check, T...) -> void;
template <typename X, typename... T>
auto bar(int, T...) -> void;
int main()
{
bar<void>(7, ""); // ambiguous according to gcc
bar<void>(7); // just fine
}
С другой стороны, clang, msvc и icc вполне довольны этим.
Какой компилятор прав?
Ссылки на соответствующие разделы стандарта предпочтительны.
Это основной вопрос 200.
Описание того, как частичное упорядочение шаблонных функций
определенный в 14.5.6.2 [temp.func.order] параграфы 3-5 не делает
любое положение для не выведенных параметров шаблона. Например,
вызов функции в следующем коде неоднозначен, хотя один
Шаблон «очевидно» более специализирован, чем другие:template <class T> T f(int); template <class T, class U> T f(U); void g() { f<int>(1); }
Причина в том, что ни один из списка параметров функции не допускает параметр шаблона
T
быть выведенным; оба вычета проваливаются, поэтому ни
шаблон считается более специализированным, чем другой, и
вызов функции неоднозначен.
Разрешение основной вопрос 214, который этот был сокращен, введен [Temp.deduct.partial] / 11:
В большинстве случаев все параметры шаблона должны иметь значения для успешного вывода, но для целей частичного упорядочения параметр шаблона может остаться без значения при условии, что он не используется в типах, используемых для частичного упорядочения.
Очевидно, что реализация этой формулировки в GCC дает сбой, когда в игру вступают пакеты.
ИМХО Я считаю, что GCC не прав, а CLANG здесь прав. Я постараюсь обосновать свою претензию ниже:
В соответствии со стандартом §14.8.3 / p1 Разрешение перегрузки [temp.over] (Акцент на шахте):
Шаблон функции может быть перегружен (без шаблона)
[Пример:
функции его имени или (другими) шаблонами функций того же
название. Когда обращение к этому имени написано (явно или неявно
используя обозначение оператора), вывод аргумента шаблона (14.8.2)
и проверка любых явных аргументов шаблона (14.3)
для каждого шаблона функции найти значения аргумента шаблона (если
любой), который может использоваться с этим шаблоном функции для создания экземпляра
специализация шаблона функции, которая может быть вызвана с помощью вызова
аргументы. Для каждого шаблона функции, если вычет аргумента и
проверка успешна, аргументы шаблона (выводимые и / или явные)
используются для синтеза объявления одного шаблона функции
специализация, которая добавляется к функциям-кандидатам
используется в разрешении перегрузки. Если для данного шаблона функции,
Сбой вывода аргумента или шаблон синтезированной функции
специализация будет плохо сформирована, такая функция не будет добавлена к
набор функций-кандидатов для этого шаблона. Полный набор
Функции-кандидаты включают в себя все синтезированные объявления и все
не шаблонных перегруженных функций с тем же именем.
Синтезированные объявления обрабатываются как любые другие функции в
остаток разрешения перегрузки, за исключением случаев, явно указанных в
13.3.3.144template<class T> T max(T a, T b) { return a>b?a:b; } void f(int a, int b, char c, char d) { int m1 = max(a,b); // max(int a, int b) char m2 = max(c,d); // max(char a, char b) int m3 = max(a,c); // error: cannot generate max(int,char) }
144) Параметры специализаций шаблонов функций содержат
нет типов параметров шаблона. Набор преобразований, разрешенных на вывод
аргументы ограничены, потому что процесс вывода аргумента производит
шаблоны функций с параметрами, которые соответствуют вызову
аргументы точно или отличаются только таким образом, что может быть преодолено
разрешены ограниченные конверсии. Не выводимые аргументы позволяют полностью
Диапазон конверсий. Также обратите внимание, что 13.3.3 указывает, что
не шаблонная функция получит преимущество перед шаблоном
специализация, если две функции в остальном одинаково хороши
кандидаты на матч с перегрузкой.
Из вышесказанного мы получаем, что явные аргументы шаблона будут проверены и, если проверка пройдет успешно, будут использованы для синтеза специализации, которая будет добавлена к функциям-кандидатам для разрешения перегрузки. Таким образом, тот факт, что вы указываете явно X
не имеет отношения к процессу.
Также из стандарта C ++ §13.3.3 / p1.7 Лучшая жизнеспособная функция [over.match.best]:
F1
а такжеF2
являются специализациями шаблона функции, а функция
шаблон дляF1
более специализирован, чем шаблон дляF2
в соответствии с правилами частичного заказа, описанными в 14.5.6.2.
Теперь из §14.5.6.2 / p3 Частичное упорядочение шаблонов функций [temp.func.order] мы получаем, что при частичном упорядочивании параметров пакеты также входят в игру, так что здесь нет проблем.
Сейчас:
template <typename X, typename... T>
auto bar(int, T...) -> void;
более специализирован, чем:
template <typename X, typename Check, typename... T>
auto bar(Check, T...) -> void;
Поэтому зову:
bar<void>(7, "");
не является двусмысленным.
Исходя из вышеизложенного, я считаю, что это ошибка GCC.