Частичное упорядочение по T *... и const T &
cppreference утверждает следующее
template <class ...T> int f(T*...); // #1
template <class T> int f(const T&); // #2
f((int*)0); // OK: selects #1
// (was ambiguous before DR1395 because deduction failed in both directions)
Если мы следуем DR1395, мы видим
Если A был преобразован из пакета параметров функции, а P не является пакетом параметров, вывод типа завершается ошибкой. В противном случае, с помощью Используя полученные типы Р и, вычет затем сделано, как описано в 17.9.2.5 [temp.deduct.type]. Если P является пакетом параметров функции, тип A каждого оставшегося типа параметра шаблона аргумента сравнивается с типом P идентификатора объявления пакета функциональных параметров. Каждое сравнение выводит аргументы шаблона для последующих позиций в пакетах параметров шаблона, расширенных пакетом параметров функции. Аналогично, если A был преобразован из пакета параметров функции, он сравнивается с каждым оставшимся типом параметра шаблона параметра. Если для данного типа вывод выполняется успешно, считается, что тип из шаблона аргумента является по меньшей мере таким же специализированным, как и тип из шаблона параметра.
[...]
Если после рассмотрения вышеизложенного шаблон функции F по меньшей мере столь же специализирован, как и шаблон функции G, и наоборот, и если G имеет пакет конечных параметров, для которого F не имеет соответствующего параметра, и если у F нет завершающего Пакет параметров, то F более специализирован, чем G.
Из того, что я могу сделать вывод, это означает, что мы должны сопоставлять каждый отдельный тип, расширенный от T*...
до const T&
и наоборот. В этом случае T*
более специализирован, чем const T&
(T
из U*
завершается успешно, T*
из U
завершается неудачей).
Однако составители не согласны. Кланг считает, что это неоднозначно, а gcc считает, что следует вызвать второе, оба из которых отличаются от cppreference.
Какое правильное поведение?
Ответы
Ответ 1
cppreference является правильным для этого примера (который является точно примером из проблемы CWG, а также CWG 1825). Пусть пройдут дедукцию в обе стороны.
Вывести template <class...T> int f(T*...);
от const U&
. Это не удастся, и мы не сможем вывести T*
из const U&
- тот факт, что это пакет здесь, является несущественным. Так что # 2 не настолько специализирован, как # 1.
Вывести template <class T> int f(const T&);
из U*...
Раньше у нас было правило: "Если A
был преобразован из пакета параметров функции, а P
не является пакетом параметров, вывод типа завершается неудачей". - что означало бы, что мы потерпели неудачу, даже не пытаясь сделать что-то еще. Но CWG 1395 удалила это предложение, поэтому мы продолжаем, и у нас есть новое предложение:
Аналогично, если A был преобразован из пакета параметров функции, он сравнивается с каждым оставшимся типом параметра шаблона параметра.
Мы сравниваем U*
отдельно с каждым оставшимся типом параметра, который является const T&
. Этот вывод успешен. Так что # 1, по крайней мере, так же специализирован, как # 2.
В результате, теперь № 1 более специализирован, чем № 2. Цитата, которую вы приводите в отношении конечных пакетов параметров, поскольку более поздний разрыв связей не применим, поскольку у нас нет случая, когда каждый шаблон функции, по крайней мере, столь же специализирован, как и другой. Также другая цитата, которую вы цитируете ([temp.deduct.type]/10, касается определения типов функций, поэтому я не думаю, что это применимо и здесь? Хотя я также не уверен насчет примера в этом разделе - или что это конкретное правило на самом деле означает.