#c #language-lawyer
#c #язык-юрист
Вопрос:
cppreference утверждает следующее
template <class ...T> int f(T*...); // #1
template <class T> int f(const Tamp;); // #2
f((int*)0); // OK: selects #1
// (was ambiguous before DR1395 because deduction failed in both directions)
Если мы последуем за DR1395, мы увидим
Если A был преобразован из пакета параметров функции, а P не является пакетом параметров, вывод типа завершается ошибкой. В противном случае, используяИспользуя результирующие типы P и A, затем выполняется вычет, как описано в 17.9.2.5 [temp.deduct.type]. Если P является пакетом параметров функции, тип A каждого оставшегося типа параметра шаблона аргумента сравнивается с типом P идентификатора декларатора пакета параметров функции. Каждое сравнение выводит аргументы шаблона для последующих позиций в пакетах параметров шаблона, расширенных пакетом параметров функции. Аналогично, если A был преобразован из пакета параметров функции, он сравнивается с каждым оставшимся типом параметра шаблона параметра. Если дедукция завершается успешно для данного типа, тип из шаблона аргумента считается, по крайней мере, таким же специализированным, как тип из шаблона параметра.[…]
Если после рассмотрения вышеизложенного шаблон функции F по крайней мере такой же специализированный, как шаблон функции G, и наоборот, и если G имеет завершающий пакет параметров, для которого у F нет соответствующего параметра, и если у F нет завершающего пакета параметров, то F является более специализированным, чем G.
Из того, что я могу сделать вывод, это означает, что мы должны сопоставлять каждый отдельный тип, расширенный от T*...
к const Tamp;
, и наоборот. В этом случае T*
является более специализированным, чем const Tamp;
( T
from U*
завершается успешно, T*
from U
завершается неудачей).
Однако компиляторы не согласны. Clang считает, что это неоднозначно, а gcc считает, что следует вызвать второе, оба из которых отличаются от cppreference .
Каково правильное поведение?
Комментарии:
1. Совершенно неудивительно, что компиляторы отстают в реализации решений основных проблем.
Ответ №1:
cppreference корректен для этого примера (который в точности соответствует примеру из выпуска CWG, а также CWG 1825). Давайте сделаем вывод обоими способами.
Выводите template <class ...T> int f(T*...);
из const Uamp;
. Это не удается, T*
из const Uamp;
не удастся вывести — тот факт, что это пакет здесь, не имеет значения. Таким образом, # 2, по крайней мере, не так специализирован, как # 1.
Выводить template <class T> int f(const Tamp;);
из U*...
У нас было правило «Если A
было преобразовано из пакета параметров функции и P
не является пакетом параметров, вывод типа завершается ошибкой». — что означало бы, что мы потерпели неудачу еще до того, как попытались сделать что-либо еще. Но CWG 1395 удалил это предложение, поэтому мы продолжаем, и у нас есть новое предложение:
Аналогично, если A был преобразован из пакета параметров функции, он сравнивается с каждым оставшимся типом параметра шаблона параметра.
Мы сравниваем U*
, отдельно, с каждым оставшимся типом параметра, который является const Tamp;
. Этот вывод выполнен успешно. Таким образом, # 1, по крайней мере, такой же специализированный, как # 2.
В результате теперь # 1 более специализирован, чем # 2. Цитата, которую вы приводите о завершающих пакетах параметров в качестве более позднего промежуточного решения, неприменима — поскольку у нас нет случая, когда каждый шаблон функции по крайней мере столь же специализирован, как и другой. Также другая цитата, которую вы приводите ([temp.deduct.type] / 10, касается вывода типов функций, поэтому я не думаю, что это применимо и здесь? Хотя я также не уверен в примере в этом разделе — или в том, что на самом деле означает это конкретное правило.
Комментарии:
1. Если это так, как мне достичь того, чтобы
f(T, Ts...)
быть более специализированным, чемf(Us...)
?2. После повторного прочтения [temp.deduct.type] это, по-видимому, действительно не имеет отношения к вопросу. Речь идет о том, как параметры шаблона выводятся из типа, составленного из них, например, как
void(int)
совпадают сvoid(T)
.3. @Передайте это… Знаете что, я понятия не имею.
4. @T.C. Нет, я просто забыл, какой из них был # 1, а какой # 2 😀
5. Это
T, Ts...
/Us...
ошибка с разрешением, о которой было сообщено в core reflector еще в январе 2017 года.