C++ различные методы шаблонов, вызываемые с одной переменной
Может кто-нибудь объяснить, почему однажды используется метод c(T*)
и в следующий раз d<>(int*)
? методы c
и d
кажутся мне идентичными, и я не могу понять, почему не называется тот же тип метода.
#include <iostream>
using namespace std;
template<typename T>
void c(T){ cout <<"(T)" << endl; }
template<>
void c<>(int*){ cout <<"(int*)" << endl; }
template<typename T>
void c(T*){ cout <<"(T*)" << endl; }
template<typename T>
void d(T){ cout <<"(T)" << endl; }
template<typename T>
void d(T*){ cout <<"(T*)" << endl; }
template<>
void d<>(int*){ cout <<"(int*)" << endl; }
int main(){
int i;
c(&i);
d(&i);
return 0;
}
Вывод:
(T*)
(int*)
Ответы
Ответ 1
Вы просто наткнулись на уродливую часть С++.
Передача разрешения перегрузки во время компиляции - это поиск лучшей перегрузки для текущего кода. Он выполняется на наборе шаблонов функций и функций, который был выбран на этапе поиска и направлен на определение одной (и только одной) перегрузки, которая лучше других.
Для шаблонов функций они разделяются на две группы:
- "базовые" шаблоны функций
- специализированные шаблоны функций
и процесс разрешения перегрузки имеет два шага:
- Выберите наилучшее соответствие между регулярными функциями и шаблонами функций "base"
- Если на шаге 1 выбран шаблон "базовой" функции, выберите лучшую специализацию (если есть какие-либо совпадения, в противном случае используйте "base" )
В обоих примерах лучшая "базовая" функция - c(T*)
и d(T*)
, поэтому это второй шаг, который отличается. Почему?
Поскольку, чтобы быть специализацией шаблона функции, этот шаблон функции должен быть объявлен первым.
Таким образом:
-
c<>(int*)
является специализацией c(T)
-
d<>(int*)
является специализацией d(T*)
и, следовательно, когда c(T*)
выбирается на этапе 1., тогда нет лучшей специализации, а когда d(T*)
выбрано, d<>(int*)
- лучшая специализация.
Поскольку это сложно, рекомендация экспертов... НЕ использовать специализированную функцию. Он просто смешивается с перегрузкой шаблона функции.
Ответ 2
template <typename T>
void c(T); // 1: function template
template <>
void c<>(int*); // 2: specialization of 1
template<typename T>
void c(T*); // 3: overload of 1
template<typename T>
void d(T); // 4: function template
template<typename T>
void d(T*); // 5: overload of 4
template<>
void d<>(int*); // 6: specialization of 5
// ...
int i;
c(&i); // 3 is more appropriate overload than 1
d(&i); // 5 is more appropriate overload than 4
// and it has the suitable specialization 6
Диаграмма:
c d
/ \ / \
overloads 1 (3) 4 (5) |
| | | priority
specializations 2 (6) V
Ответ 3
Разрешение перегрузки выбирает только базовый шаблон (или функцию без функции, если таковая имеется). Только после того, как будет решено, какой базовый шаблон будет выбран, и этот выбор заблокирован, будет ли компилятор оглядываться вокруг, чтобы увидеть, есть ли подходящая специализация этого шаблона, и если это поможет специализации.
Для функции c
специализация void c<>(int*)
предназначена для перегрузки void c(T)
, а для d
специализация void d<>(int*)
предназначена для перегрузки void d(T*)
.
Таким образом, в приведенном выше объяснении сначала void c(T)
проверяется и игнорируется над лучшей перегрузкой void c(T*)
(которая не имеет специализаций), которая печатается. Для d
тоже перегрузка void d(T*)
заблокирована, но затем отмечена специализация void d(int*)
и затем выбирается.