Читая стандарт C ++ 11, я не могу полностью понять смысл следующего утверждения. Пример очень приветствуется.
Два набора типов используются для определения частичного упорядочения. Для каждого
из вовлеченных шаблонов есть оригинальный тип функции и
преобразованный тип функции. [Примечание: создание преобразованного типа
описано в 14.5.6.2. — примечание конца] В процессе удержания используется
преобразованный тип в качестве шаблона аргумента и исходный тип
другой шаблон в качестве шаблона параметра. Этот процесс делается дважды
для каждого типа, участвующего в сравнении частичного упорядочения: один раз с
преобразованный шаблон-1 в качестве шаблона аргумента и шаблон-2 в качестве шаблона
шаблон параметра и снова с использованием преобразованного шаблона-2 в качестве
шаблон аргумента и шаблон-1 в качестве шаблона параметра
— N3242 14.8.2.4.2
В то время как Ксео дал довольно хорошее описание в комментариях, Я постараюсь дать пошаговое объяснение с рабочим примером.
Прежде всего, первое предложение из абзаца, который вы цитировали, гласит:
Для каждого из задействованных шаблонов есть исходный тип функции и преобразованный тип функции. […]
Держись, что это?преобразованный тип функции«? Пункт 14.5.6.2/3 объясняет, что:
Создание преобразованного шаблона для каждого типа, не типового или шаблонного параметра шаблона (включая
пакеты параметров шаблона (их 14.5.3) синтезируют уникальный тип, значение или шаблон класса соответственно
и заменить его для каждого вхождения этого параметра в тип функции шаблона […]
Это формальное описание может показаться неясным, но на самом деле это очень просто. Давайте возьмем этот шаблон функции в качестве примера:
template<typename T, typename U>
void foo(T, U) // #1
Сейчас с T
а также U
являются параметрами типа, вышеприведенный абзац просит нас выбрать соответствующий аргумент типа для T
(что угодно) и заменить его везде в сигнатуре функции где T
затем сделать то же самое для U
,
Сейчас «синтезировать уникальный тип«означает, что вы должны выбрать вымышленный тип, который вы нигде не использовали, и мы могли бы назвать это P1
(а затем выбрать P2
за U
), но это сделало бы наше обсуждение бесполезным формальным.
Давайте просто упростим вещи и выберем int
за T
а также bool
за U
— мы не используем эти типы где-либо еще, поэтому для наших целей они так же хороши, как P1
а также P2
,
Итак, после преобразования имеем:
void foo(int, bool) // #1b
Это преобразованный тип функции для нашего оригинала foo()
шаблон функции.
Итак, давайте продолжим толковать абзац, который вы цитировали. Второе предложение гласит:
В процессе дедукции используется преобразованный тип в качестве шаблона аргумента и исходный тип другой шаблон в качестве шаблона параметра. […]
Чего ждать «другой шаблон«У нас есть только одна перегрузка foo()
до сих пор. Правильно, но для того, чтобы установить порядок между шаблонами функций, нам нужно как минимум два из них, поэтому нам лучше создать второй. Давайте использовать:
template<typename T>
void foo(T const*, X<T>) // #2
куда X
это какой-то наш классовый шаблон.
Что теперь с этим вторым шаблоном функции? Ах, да, нам нужно сделать то же самое, что мы делали ранее для первой перегрузки foo()
и преобразовать его: так что давайте снова выберем аргумент типа для T
и заменить T
везде. Я выберу char
на этот раз (мы не будем использовать его где-либо еще в этом примере, так что это так же хорошо, как некоторые вымышленные P3
):
void foo(char const*, X<char>) #2b
Отлично, теперь у него есть два шаблона функций и соответствующие преобразованные типы функций. Так как же определить #1
более специализированный, чем #2
или наоборот?
Из вышеприведенного предложения мы знаем, что исходные шаблоны и их преобразованные типы функций должны как-то совпадать. Но как? Вот что объясняет третье предложение:
Этот процесс выполняется дважды для каждого типа, участвующего в сравнении частичного упорядочения: один раз с использованием преобразованного шаблона-1 в качестве шаблона аргумента и шаблона-2 в качестве шаблона параметра и снова с использованием преобразованного шаблона-2 в качестве шаблона аргумента и шаблона-1 в качестве шаблона параметра
Так что в основном трансформировали тип функции первого шаблона (#1b
) сопоставляется с типом функции оригинал второй шаблон (#2
). И, конечно, наоборот, трансформировали тип функции второго второго шаблона (#2b
) сопоставляется с типом функции оригинал первый шаблон (#1
).
Если сопоставление будет успешным в одном направлении, но не в другом, то мы будем знать, что один из шаблонов более специализирован, чем другой. В противном случае, ни один из них не является более специализированным.
Давайте начнем. Прежде всего, мы должны соответствовать:
void foo(int, bool) // #1b
против:
template<typename T>
void foo(T const*, X<T>) // #2
Есть ли способ, которым мы можем выполнить вывод типа на T
чтобы T const*
становится точно int
а также X<T>
становится точно bool
? (на самом деле, точный совпадение не является обязательным, но из этого правила действительно мало исключений, и они не имеют отношения к иллюстрации механизма частичного упорядочения, поэтому мы их проигнорируем).
Едва. Итак, давайте попробуем сопоставить наоборот. Мы должны соответствовать:
void foo(char const*, X<char>) // #2b
против:
template<typename T, typename U>
void foo(T, U) // #1
Можем ли мы вывести T
а также U
здесь, чтобы произвести точное совпадение для char const*
а также X<char>
соответственно? Конечно! Это тривиально. Мы просто выбираем T = char const*
а также U = X<char>
,
Итак, мы обнаружили, что преобразованный тип функции нашей первой перегрузки foo()
(#1b
) не может быть сопоставлено с исходным шаблоном функции нашей второй перегрузки foo()
(#2
); с другой стороны, преобразованный тип функции второй перегрузки (#2b
) Можно быть сопоставленным с исходным шаблоном функции первой перегрузки (#1
).
Заключение? Вторая перегрузка foo()
более специализирован, чем первый.
Чтобы выбрать контрпример, рассмотрим эти два шаблона функций:
template<typename T, typename U>
void bar(X<T>, U)
template<typename T, typename U>
void bar(U, T const*)
Какая перегрузка более специализированная, чем другая? Я не буду повторять всю процедуру снова, но вы можете сделать это, и это должно убедить вас, что совпадение не может быть произведено ни в одном направлении, поскольку первая перегрузка является более специализированной, чем вторая, в отношении первого параметра, но второй более специализирован, чем первый, в отношении второго параметра.
Заключение? Ни одна из функций шаблона не является более специализированной, чем другая.
Теперь в этом объяснении я проигнорировал множество деталей, исключений из правил и загадочных отрывков в Стандарте, но механизм, описанный в приведенном вами абзаце, действительно является этим.
Также обратите внимание, что тот же механизм, описанный выше, используется для создания «более-специализированы, чем«упорядочение между частичными специализациями учебный класс шаблон, сначала создав соответствующий шаблон фиктивной функции для каждой специализации, а затем упорядочив эти шаблоны функций с помощью алгоритма, описанного в этом ответе.
Это указано в пункте 14.5.5.2/1 стандарта C ++ 11:
Для двух частичных специализаций шаблона класса первая, по крайней мере, такая же специализированная, как вторая, если, Учитывая
после переписывания двух шаблонов функций первый шаблон функции по меньшей мере так же специализирован, как и второй
согласно правилам заказа для шаблонов функций (14.5.6.2):— первый шаблон функции имеет те же параметры шаблона, что и первая частичная специализация, и имеет
единственный параметр функции, тип которого является специализацией шаблона класса с аргументами шаблона
первой частичной специализации, и— шаблон второй функции имеет те же параметры шаблона, что и вторая частичная специализация
и имеет единственный параметр функции, тип которого является специализацией шаблона класса с шаблоном
аргументы второй частичной специализации.
Надеюсь, это помогло.
Других решений пока нет …