Подтвердить что ты не робот

Этот случай перегрузки функции шаблона ускользает от моего понимания

#include <iostream>

template<typename T>
struct identity
{
    typedef T type;
};

template<typename T> void bar(T) { std::cout << "a" << std::endl; }
template<typename T> void bar(typename identity<T>::type) { std::cout << "b" << std::endl; }

int main ()
{
    bar(5); // prints "a" because of template deduction rules
    bar<int>(5); // prints "b" because of ...?

    return EXIT_SUCCESS;
}

Я ожидал, что bar<int>(5) приведет к двусмысленности, по крайней мере. Какое безумное правило о разрешении перегрузки функции шаблона здесь?

4b9b3361

Ответ 1

Как только мы получим наши функции-кандидаты (оба bar s), а затем сведем их к жизнеспособным функциям (все еще обе bar s), мы должны определить наилучшую жизнеспособную функцию. Если их больше одного, мы получаем ошибку двусмысленности. Шаги, которые мы предпринимаем для определения наилучшего, изложены в [over.match.best]:

[A] жизнеспособная функция F1 определена как лучшая функция, чем другая жизнеспособная функция F2, если для всех аргументов i, ICS i (F1) не является худшей последовательностью преобразования, чем ICS i (F2), а затем - для некоторого аргумента j ICS j (F1) является лучшей последовательностью преобразования, чем ICS j (F2), или, если не это,

Обе функции принимают аргумент типа int, поэтому обе последовательности преобразования идентичны. Мы продолжаем.

- контекст является инициализацией по пользовательскому преобразованию [...]

Не применяется.

- контекст представляет собой инициализацию функцией преобразования для привязки прямого ссылки (13.3.1.6) ссылки на тип функции, [...]

Не применяется.

- F1 не является специализированной функцией шаблона, а F2 является специализированной функцией шаблона или, если не это,

Оба bar<int> являются специализированными шаблонами функций. Поэтому мы переходим к самой последней точке маркера, чтобы определить лучшую жизнеспособную функцию.

- F1 и F2 - специализированные шаблоны функций, а шаблон функции для F1 - более специализированный чем шаблон для F2 в соответствии с правилами частичного упорядочения, описанными в 14.5.6.2.

Частичные правила упорядочения в основном сводятся к тому, что мы синтезируем новые уникальные типы для аргументов как перегрузок bar, так и вычитания шаблонов при другой перегрузке.

Сначала рассмотрим перегрузку "b". Синтезируйте тип typename identity<Unique1>::type и попытайтесь выполнить вычитание шаблона против T. Это удается. Простейший листинг шаблона вывода есть.

Затем рассмотрим "a" перегрузку. Синтезируйте тип Unique2 и попытайтесь выполнить вывод шаблона против typename identity<T>::type. Это не удается! Это не выведенный контекст - вычет не может быть успешным.

Так как вычет типа шаблона преуспевает только в одном направлении, перегрузка bar(typename identity<T>::type) считается более специализированной и выбирается как лучший жизнеспособный кандидат.


bogdan представляет еще один интересный случай для рассмотрения частичного заказа. Вместо этого сравните:

template <typename T> void bar(T, T); // "c"
template <typename T> void bar(T, typename identity<T>::type ); // "d"

bar(5,5);
bar<int>(5, 5);

Опять же, оба кандидата жизнеспособны (на этот раз даже без явного указания T), поэтому мы рассмотрим правило частичного упорядочения.

Для "c" перегрузки мы синтезируем аргументы типа UniqueC, UniqueC и пытаемся выполнить вычет против T, typename identity<T>::type. Это выполняется успешно (с T == UniqueC). Таким образом, "c" по крайней мере так же специализирован, как "d" .

Для "d" перегрузки мы синтезируем аргументы типа UniqueD, typename identity<UniqueD>::type и пытаемся выполнить вывод на T, T. Это не удается! Аргументы имеют разные типы! Таким образом, "d" не является, по меньшей мере, таким же специализированным, как "c".

Таким образом, вызывается перегрузка "c".