Во-первых, мы выбираем жизнеспособных кандидатов. Это:
void f(MyClass, int); // with X=MyClass, Y=int
void f(MyClass, typename MyClass::type); // with X=MyClass
Эти кандидаты принимают одинаковые аргументы, поэтому имеют эквивалентные последовательности преобразования. Так что ни один из tiebreakers на основе тех, не применяются, поэтому мы падаем назад к последнему возможному тай-брейку в [over.match.best]:
Учитывая эти определения, жизнеспособная функция F1 определяется, чтобы быть лучше, чем функцией другая жизнеспособная функция F2, если для всех аргументов i, ICSi (F1) не хуже схемы преобразования, чем ICSi (F2), а затем [...] F1 и F2 являются специализированными шаблонами функций, а шаблон функции для F1 больше специализированный , чем шаблон для F2 в соответствии с правилами частичного заказа, описанными в 14.5.6.2.
Итак, мы пытаемся заказать два шаблона функций на основе правил частичного упорядочения, которые включают в себя синтез уникального типа для каждого параметра шаблона и попытку выполнить вычитание шаблона против каждой перегрузки. Но с ключом дополнительным соответствующим правилом из [temp.deduct.partial]:
Каждого типа номинированный выше из шаблона параметров и соответствующего типа из шаблона аргумента используются в качестве типов Р и А. Если конкретный P не содержит шаблонных параметров, участвующих в выводе аргумента шаблона, то P не используется для определения порядка.
Так что это значит. Сначала попробуем вывести общую версию из перегрузки. Мы выбираем синтетические типы UniqueX
(для X
) и UniqueX_type
(для typename X::type
) и видим, можем ли мы вызвать общую функцию. Это успешно (с X=UniqueX
и Y=typename X::type
).
Давайте попробуем инверсию. Мы выбираем UniqueX
(для X
) и UniqueY
(для Y
) и попытаемся выполнить вычет шаблонов. Для первой пары P/A это тривиально преуспевает. Но для второго аргумента X
- это не выведенный контекст, который, по вашему мнению, будет означать, что вычет шаблона не удался. BUT согласно смещенной части цитаты, мы просто пропустим эту пару P/A для целей заказа. Итак, поскольку первая пара P/A преуспела, мы считаем, что весь процесс дедукции преуспел.
Поскольку удержание шаблона преуспевает в обоих направлениях, мы не можем выбрать одну или более функцию как более специализированную.Поскольку нет дальнейших тай-брейков, нет ни одного лучшего жизнеспособного кандидата, поэтому вызов неоднозначен.
Когда вторая перегрузка изменяется на:
template <class X> void f(X, int);
часть процесса, который изменяется в том, что вычет в настоящее время терпит неудачу в одном направлении. Мы можем вывести X=UniqueX
, но вторая пара имеет параметр типа int
и аргумент типа UniqueY
, который не будет работать, поэтому это направление терпит неудачу. В обратном направлении мы можем вывести X=UniqueX
и Y=int
. Это делает эту перегрузку более специализированной, чем общая перегрузка, поэтому было бы желательно, чтобы последний тай-брейкер, о котором я упоминал ранее.
В качестве дополнения обратите внимание на то, что частичный порядок шаблонов является сложным. Рассмотрите:
template <class T> struct identity { using type = T; };
template <class T> void foo(T); // #1
template <class T> void foo(typename identity<T>::type); // #2
template <class T> void bar(T, T); // #3
template <class T> void bar(T, typename identity<T>::type); // #4
foo(0); // calls #1, #2 isn't even viable
foo<int>(0); // calls #2
bar(0,0); // calls #3! we fail to deduce 3 from 4, but we succeed
// in deducing 4 from 3 because we ignore the second P/A pair!
Я собирался написать тот же ответ, но застрял в той части, которая заставила вас подчеркнуть **, но ** в ваших объяснениях. Я все еще не могу переварить вторую цитату в вашем ответе. Тем не менее, я считаю, что это должен быть принятый ответ. – Leon
@Leon Первоначально выбрала неправильную цитату, моя вина. Новый - это тот, который я хотел включить. – Barry