Что такое процедура частичного заказа при вычете шаблона

Читая стандарт C ++ 11, я не могу полностью понять смысл следующего утверждения. Пример очень приветствуется.

Для определения частичного упорядочивания используются два набора типов. Для каждого из задействованных шаблонов существует исходный тип функции и тип преобразованной функции. [Примечание: создание преобразованного типа описано в 14.5.6.2. - конец примечания] В процессе вывода преобразованный тип используется в качестве шаблона аргумента, а исходный тип другого шаблона - в качестве шаблона параметра. Этот процесс выполняется дважды для каждого типа, участвующего в сравнении частичного упорядочения: один раз с использованием преобразованного шаблона-1 в качестве шаблона аргумента и шаблона-2 в качестве шаблона параметра и снова с использованием преобразованного шаблона-2 в качестве шаблона аргумента и шаблона-1. в качестве шаблона параметра
- N3242 14.8.2.4.2


person yuan    schedule 09.06.2013    source источник
comment
Вы уже смотрели лекции Стефана Т. Лававежа? на Core C ++? В частности, могут быть полезны лекции 2 и 3 о выводе аргументов и разрешении перегрузки.   -  person TemplateRex    schedule 09.06.2013
comment
Частичное упорядочение в основном проверяет параметры двух шаблонов, если параметр одного является более строгим, чем соответствующий параметр другого. Если у вас есть f(T) и f(bar<T>)T в качестве параметра шаблона), то первая перегрузка может принимать все возможные аргументы второй перегрузки, но вторая перегрузка не может принимать все возможные аргументы от первой перегрузки - только аргументы bar<T> форма.   -  person Xeo    schedule 09.06.2013


Ответы (1)


Хотя Xeo дал довольно хорошее описание в комментариях , Я постараюсь дать пошаговое объяснение на рабочем примере.

Прежде всего, первое предложение из процитированного вами абзаца гласит:

Для каждого из задействованных шаблонов существует исходный тип функции и преобразованный тип функции. [...]

Погодите, что это за «преобразованный тип функции»? В пункте 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):

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

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

Надеюсь, это помогло.

person Andy Prowl    schedule 09.06.2013
comment
+1, но вся эта игра с выводом аргументов происходит после поиска имени и применяется только к перегрузкам функций / специализациям классов, то есть все они имеют одно и то же имя? так что foo(T, U) и foo(T, X<T>) будут играть друг против друга, верно? - person TemplateRex; 09.06.2013
comment
@TemplateRex: Ой, ты прав. Я отредактирую ответ, чтобы прояснить это. Спасибо, что упомянули :) - person Andy Prowl; 09.06.2013
comment
Я бы также упомянул, что сопоставление аргументов для специализаций шаблона класса решается путем создания набора уникальных перегрузок шаблона функции, а затем перехода к применению игры взаимной подстановки. Т.е. Сопоставление с образцом специализации класса идентично выводу аргументов для шаблонов функций. - person TemplateRex; 09.06.2013
comment
@TemplateRex: Да, я это добавил. - person Andy Prowl; 09.06.2013
comment
Это отличный ответ, кстати, жаль, что этот материал слишком сложен для большинства программистов на C ++, чтобы генерировать больше трафика, по сравнению, например, с vector::resize() вопросов :-) - person TemplateRex; 09.06.2013
comment
@TemplateRex: :) Да, это общий шаблон для SO. Но это еще и вопрос времени. На вопрос о resize() ответ был дан в течение нескольких минут, а на этот вопрос пришлось ждать 7 часов. Возможно, это также объясняет, почему вокруг этого больше не так много трафика. - person Andy Prowl; 09.06.2013