Как определить первичный шаблон специализации функции?

обычно довольно интуитивно понятно, что является основным шаблоном специализации шаблона функции, однако я ищу формальные правила, чтобы понять более удивительные ситуации. Например:

template <typename T, typename U>
void f(T, U) {}     // (1)

template <typename T>
void f(T, T) {}     // (2)

template <>
void f(int, int) {} // (3); specializes (2), not (1); why?

2 ответов


давайте сосредоточимся на объявлении общих шаблонов (1) и (2). Это два различных шаблона, например (2) не является специализацией (1). Хорошо, теперь, когда мы пишем специализацию:

template <>
void foo(int, int) {}

при выводе шаблона для специализации компилятор идентифицирует двух кандидатов. Тогда он должен выбрать, который best fit. Процесс такого выбора называется "частичное упорядочение шаблонов функций". Выбранный цитата:

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

назовем S набор соответствующих шаблонов. Тогда для каждой пары (f1, f2) на S компилятор преобразует f1 С применением манекена типы (соотв. значения) по его типу (соотв. не тип) параметры. Затем он пытается сопоставить его с f2. Затем он выполняет ту же процедуру, Преобразуя f2 и пытается сопоставить его с f1. В конце, пройдя через каждую пару, компилятор может определить, какой кандидат шаблона является наиболее специализированным. Если это не удается сделать, компиляция завершается неудачей.

в нашем случае у нас есть два соответствующих шаблона, поэтому мы применяем описанную процедуру сверху:

  • преобразовано (1) применительно к (2): скажем foo с T = T1 и U=T2. Он пытается соответствовать (2): вычет не удается
  • преобразованный (2), примененный к (1): foo(T1, T1), при применении к (1) он разрешается как T = T1 и U = T1.

из этой процедуры компилятор выводит, что (2) более специализирован, чем (1), и Ваша специализация идет на (2). Тот же процесс применяется во время разрешения перегрузки, когда компилятор ориентируется на особый звонок.

примером, иллюстрирующим всю эту процедуру, является следующее (взято из комментария @Yakk):

template <typename T, typename U>
void f(T, U) { std::cout << "f(1)\n"; }     // f(1)

template <typename T>
void f(T, T) { std::cout << "f(2)\n"; }     // f(2)

template <>
void f(int, int) { std::cout << "f(3)\n"; } // f(3); specializes f(2), not f(1); why?

// Now the same specialization but without any template overload...
template <typename T, typename U>
void g(T, U) { std::cout << "g(1)\n"; }     // g(1)

template <>
void g(int, int) { std::cout << "g(3)\n"; } // g(3); No ambiguity, specializes g(1)

Далее, давайте выполним несколько звонков:

f(1, 1); // Prints f(3)
f<int>(1, 1); // Prints f(3)
f<int, int>(1, 1); // Prints f(1)
g(1, 1); // Prints g(3)

g<int, int>(1, 1); // Prints g(3)

все это можно увидеть в действиях здесь-скопировано из комментария @Yakk.


предмет вроде бы шаблон 'частичная заказ'. Иллюстрированный пример можно найти здесь: какова процедура частичного заказа в шаблоне вычета