Частичное упорядочение по 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, касается определения типов функций, поэтому я не думаю, что это применимо и здесь? Хотя я также не уверен насчет примера в этом разделе - или что это конкретное правило на самом деле означает.