Частичный порядок при T * … и const T

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

2.Ранее существовавший отчет об ошибке gcc.

Ответ №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 года.