gcc против clang, msvc и icc: является ли этот вызов функции неоднозначным?

все компиляторы, которые я мог бы получить, согласны с тем, что это нормально:

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 вполне довольны этим.

какой компилятор правильно?

ссылки на соответствующие разделы стандарта предпочтительным.

2 ответов


Это основная проблема 200.

описание того, как частичное упорядочение функций шаблона определено в пункте 14.5.6.2 [temp.функция.порядок] пункты 3-5 не составляют любое положение для параметров шаблона nondeduced. Например, вызов функции в следующем коде неоднозначно, хотя шаблон "очевидно" более специализирован, чем другой:

template <class T> T f(int);
template <class T, class U> T f(U);
void g() {
    f<int>(1);
}

причина в том, что ни список параметров функции позволяет параметр шаблона T быть выведенным; оба вывода терпят неудачу, поэтому ни один шаблон является более специализированным, чем другие и вызов функции неоднозначен.

разрешение основной выпуск 214, к которому это было сведено, введено [temp.вычитать.partial] / 11:

в большинстве случаев, все параметры шаблона должны иметь значения для вычета на успех, но для частичного заказ цели параметр шаблона может оставаться без значения при условии, что он не используется в типах, используемых для частичного заказа.

по-видимому, реализация GCC этой формулировки глючит, как только пакеты вступают в игру.


IMHO я считаю, что GCC ошибается, и CLANG здесь правильный. Я попытаюсь обосновать свое требование ниже:

по стандарту §14.8.3 / P1 разрешение перегрузки [temp.конец] (Выделено Мной):

шаблон функции может быть перегружен либо (не шаблон) функции его имени или по (другим) шаблонам функций того же имя. Когда вызов этого имени записывается (явно или неявно с помощью оператор обозначения),вывод аргумента шаблона (14.8.2) и выполняется проверка любых явных аргументов шаблона (14.3) для каждого шаблона функции Найти значения аргументов шаблона (если any), который можно использовать с этим шаблоном функции для создания экземпляра специализация шаблона функции, которую можно вызвать с помощью вызова аргументы. Для каждого шаблона функции, если аргумент дедукции и проверка успешна, шаблон-аргументы (выведенные и / или явные) не использовать синтезировать объявление одного шаблона функции специализация, которая добавляется к функциям-кандидатам используется в разрешении перегрузки. If, для заданного шаблона функции, ошибка вывода аргумента или шаблон синтезированной функции специализация была бы плохо сформирована, такая функция не добавляется к набор функций-кандидатов для этого шаблона. Полный набор функции-кандидаты включают все синтезированные объявления и все из non-template перегруженные функции с тем же именем. Этот синтезированные объявления рассматриваются как любые другие функции в остальная часть разрешения перегрузки, за исключением явно отмеченного в 13.3.3.144

[пример:

template<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 указывает, что функция non-template будет иметь предпочтение перед шаблоном специализация, если две функции в противном случае одинаково хороши кандидаты на матч перегрузки.

от вышеуказанного мы получаем эти явные аргументы шаблона будут проверены, и если проверка будет успешной, то будет использоваться для синтеза специализации, которая будет добавлена к функциям-кандидатам для разрешения перегрузки. Таким образом, факт вы указываете явно X не имеет значения для процесса.

также из стандарта C++§13.3.3 / p1.7 Лучшая жизнеспособная функция [окончена.спичка.best]:

F1 и F2 специализации шаблона функции, и функция шаблон для F1 более специализирован, чем шаблон для F2 согласно частичному правила заказа описаны в 14.5.6.2.

сейчас от §14.5.6.2 / p3 частичное упорядочение шаблонов функций [temp.функция.порядок] мы получаем, что в частичных параметрах заказа пакеты также играют, поэтому здесь нет проблем.

теперь:

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.