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

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

Рассмотрим эту простую пару шаблонов функций.

template <typename T>
void foo(T& ) { std::cout << __PRETTY_FUNCTION__ << '\n'; }

template <typename C>
void foo(const C& ) { std::cout << __PRETTY_FUNCTION__ << '\n'; }

Если мы назовем foo аргументом non-const:

int i = 4;
foo(i);

Перегрузка T& предпочтительнее на основе [over.ics.rank]/3.2.6, так как выведенная ссылка int& меньше CV, чем выведенная ссылка const int&.

Однако, если мы назовем foo аргументом const:

const int ci = 42;
foo(ci);

Перегрузка const C& предпочтительнее, потому что она "более специализирована" на основе [over.match.best]/1.7. Но каковы правила для определения этого? Я понял, что вы синтезируете тип для C (назовите его M) и попытайтесь выполнить вывод на foo(M) - но это будет успешным (с T == M). Это только значение r, которое приведет к отказу вывода, но как компилятор знает, что ему нужно выбрать rvalue на этапе синтеза?

4b9b3361

Ответ 1

Отказ от ответственности: типы, которые мы рассматриваем, всегда являются типами параметров. Типы/значения/etc. фактических переданных аргументов рассматриваются исключительно при разрешении перегрузки, никогда в частичном порядке.

Частичное упорядочение рассматривает обе перегрузки в двух "поворотах", в которых один шаблон всегда является шаблоном параметра , а другой шаблон - шаблон аргумента. [Temp.deduct.partial]/2:

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

Вы должны быть знакомы с тем, как преобразуются "шаблоны". Это указано в п. 14.5.6.2/3.

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

Итак, наши (преобразованные) шаблоны аргументов

void foo( Unique1& );

void foo( Unique2 const& );

[temp.deduct.partial]/3 и /4:

Типы, используемые для определения порядка, зависят от контекста в которые выполняются в частичном порядке:

  • В контексте вызов функции, используемые типы являются теми параметрами параметров функции для который вызов функции имеет аргументы. [..]

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

Таким образом, мы имеем два оборота, и в обоих случаях мы имеем тип P и тип A:

Поворот 1:
    P1:   T const&
    A1:   Unique1&

Поворот 2:
    P2:   T&
    A2:   Unique2 const&

Но перед началом забавы некоторые преобразования также выполняются на этих типах - я сокращен [temp.deduct.partial]/5 и /7:

  • Если P или A являются ссылками, то они заменяются типом, на который они ссылаются.
  • Удаляются все CV-квалификаторы верхнего уровня.

Обратите внимание, что удаленные cv-квалификаторы "запоминаются" позже. [temp.deduct.partial]/6:

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

Таким образом, мы остаемся с

Поворот 1:
    P1:   T
    A1:   Unique1

Поворот 2:
    P2:   T
    A2:   Unique2

Теперь мы выполняем вывод - который явно преуспевает в обоих поворотах, устанавливая T=Unique[1/2]. Из [temp.deduct.partial]/8:

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

Это дает нам то, что Unique1& по крайней мере так же специализировано, как T const&, и что Unique2 const& по крайней мере так же специализирован, как T&.


Однако здесь [temp.deduct.partial]/(9.2) выполняется:

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

  • [..]; в противном случае

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

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

Наконец, [temp.deduct.partial]/10:

Шаблон функции F по меньшей мере такой же специализированный, как шаблон функции Gесли для каждой пары типов, используемых для определения порядка, тип из F по крайней мере так же специализирован, как тип из G.
F является более специализированным чем G, если F не менее специализирован как G и G не является, по меньшей мере, таким же специализированным, как F.

означает, что, поскольку тип T& не является, по крайней мере, специализированным как Unique2 const&, и мы уже установили, что T const& не менее специализирован как Unique1&, T const& -overload более специализирован, чем T& -overload.


Вышеупомянутое правило в пункте 9 в настоящее время является предметом CWG # 2088, созданного четыре месяца назад Р. Смитом:

Поздние тай-брейки для ссылок lvalue-vs-rvalue и cv-квалификация в пункте 14.8.2.4 [temp.deduct.partial] 9 приложенное

Если для данного типа вывод преуспевает в обоих направлениях (т.е.   типы идентичны после преобразований выше) и оба P и A  были ссылочными типами (до замены на тип, упомянутый   выше):

Однако это основано на ложном предположении. [...] Нам нужно решить, является ли правило "удержание успешным в обоих направлениях" или "типы идентичны". Последнее кажется более разумным.

Это не изменит результат, установленный, поскольку типы, которые мы получили, действительно идентичны.