Частичное упорядочение шаблонов функций, содержащих пакеты параметров шаблона

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

template<class...> struct Tuple { };
template<          class... Types> void g(Tuple<Types ...>);        // #1
template<class T1, class... Types> void g(Tuple<T1, Types ...>);    // #2
template<class T1, class... Types> void g(Tuple<T1, Types& ...>);   // #3

g(Tuple<>());                     // calls #1
g(Tuple<int, float>());           // calls #2
g(Tuple<int, float&>());          // calls #3
g(Tuple<int>());                  // calls #3

Выше приведено частичное упорядочение шаблонов функций перегрузки. Я не совсем понимаю почему g(Tuple<int>()); // calls #3, Конкретно почему не могу #2 называться? Ниже приведены мои рассуждения, пожалуйста, укажите на любые ошибки:

Примечание: я буду игнорировать #1 б / к это хорошо здесь объяснено

Шаг 1: вычитание, замещение и разрешение перегрузки дают следующее:

  • void (Tuple) [T1 = int, Types пусто] # 2
  • void (Tuple) [T1 = int, Types& is empty] #3

Шаг 2: Преобразование обоих шаблонов функций:

  • void g (Tuple);
  • void g (Tuple);

Шаг 3: Это контекст вызова функции, типы - это те типы параметров функции, для которых вызов функции имеет аргументы:

  • Выведите Tuple из Tuple OK [T1 = C2; Типы... = Упаковка &...]
  • Выведите Tuple из Tuple) Хорошо? [T1 = C1; а как насчет типов &...? Это можно вывести из Pack1...? Ссылка здесь опущена?]

3) Если P является ссылочным типом, для вывода используется тип, на который ссылается P. Это выглядит хорошо.

Если P имеет одну из форм, которые включают список параметров шаблона или , то каждый элемент Pi этого списка аргументов шаблона сопоставляется с соответствующим аргументом шаблона Ai его A. Если последний Pi является расширением пакета затем его шаблон сравнивается с каждым оставшимся аргументом в списке аргументов шаблона A. Конечный пакет параметров, который не выводится иным образом, выводится в пустой пакет параметров.

Шаг 4: Если последний шаг правильный. Тогда это означает, что № 3 не более специализирован, чем № 2. Поэтому неоднозначно, к какому шаблону функции следует обращаться.

Обновление: я думаю, что я неправильно понял соответствующие цитаты выше. Когда мы сопоставляем параметры шаблона в P с аргументами шаблона в A, они сопоставляются дословно, что означает, что все преобразования и анализ, выполненные для параметров и аргументов вызова функции, не применяются снова, когда мы сопоставляем параметры / аргументы шаблона в P/A(фактически параметр / аргументы вызова функции). Тогда на шаге 3 не получится вывести Tuple< T1, Types&...> от Tuple< C1, Pack1...>), Так #3 более специализированный

1 ответ

Решение

Первым шагом в разрешении перегрузки является поиск всех кандидатов. Для вызова g(Tuple<int>())Есть три жизнеспособных кандидата:

g(Tuple<int>); // #1: [Types = {int}]
g(Tuple<int>); // #2: [T1 = int, Types = {}]
g(Tuple<int>); // #3: [T1 = int, Types = {}]

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

Таким образом, мы оставили частичное упорядочение шаблона функции. Мы синтезируем типы для каждой из перегрузок и пытаемся выполнить вывод шаблона с нашими синтезированными типами против каждой из других перегрузок. Более простое сравнение - (1) против (2) и (1) против (3). Там у нас есть [temp.deduct.partial]:

Если A был преобразован из пакета параметров функции, а P не является пакетом параметров, вывод типа завершается ошибкой.

Поскольку мы преобразуем Types... в UniqA...и первый параметр T1тип вычета не удается. Это делает (2) и (3) оба более специализированными, чем (1). Итак, теперь мы сравним (2) и (3).

Во-первых, можем ли мы вывести (2) из ​​(3)?

template <class T1, class... Types> void g(Tuple<T1, Types...> );

g(Tuple<Uniq3, Uniq3Pack&...>());

Конечно, без проблем T1 == Uniq3 а также Types... == Uniq3Pack&..., Далее мы попробуем другое направление:

template <class T1, class... Types> void g(Tuple<T1, Types&...> );

g(Tuple<Uniq2, Uniq2Pack...>());

Это не удается, так как Uniq2Pack не пачка справочных типов и Types&... является. Поскольку удержание выполняется только в одном направлении, это делает (3) более специализированную перегрузку.

Поскольку это последний стоящий, (3) является лучшим жизнеспособным кандидатом. Это может показаться нелогичным, поскольку мы на самом деле не называем его ссылочным типом, но это по-прежнему лучший выбор.

Другие вопросы по тегам