#c #templates #template-specialization
#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(amp;i);
d(amp;i);
return 0;
}
Вывод:
(T*)
(int*)
Комментарии:
1. Они отличаются порядком.
2. Методы упорядочения могут иметь какое-либо влияние?
3. Конечно, они могут. См. Почему бы не специализировать шаблоны функций? Саттер, который именно об этом.
Ответ №1:
Вы только что наткнулись на уродливую часть C .
Этап разрешения перегрузки во время компиляции заключается в поиске наилучшей перегрузки для текущего кода. Он выполняется на наборе шаблонов функций и функций, который был выбран на этапе поиска, и направлен на выявление одной (и только одной) перегрузки, которая лучше других.
Для шаблонов функций они разделены на две группы:
- Шаблоны «базовых» функций
- специализированные шаблоны функций
и процесс разрешения перегрузки состоит из двух этапов:
- Выберите наилучшее соответствие между шаблонами обычной функции и «базовой» функции
- Если на шаге 1 выбран «базовый» шаблон функции, выберите наилучшую специализацию (если она совпадает, в противном случае используйте «базовый»)
В обоих ваших примерах лучшей «базовой» функцией является c(T*)
and 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(amp;i); // 3 is more appropriate overload than 1
d(amp;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
Комментарии:
1. Это очень краткий способ представления информации, приятно 🙂
2. @MatthieuM. Спасибо.
Ответ №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*)
отмечается специализация, а затем выбирается.