Как определить первичный шаблон специализации функции?
обычно довольно интуитивно понятно, что является основным шаблоном специализации шаблона функции, однако я ищу формальные правила, чтобы понять более удивительные ситуации. Например:
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.
предмет вроде бы шаблон 'частичная заказ'. Иллюстрированный пример можно найти здесь: какова процедура частичного заказа в шаблоне вычета