C разные шаблонные методы, вызываемые для одной и той же переменной

#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. Выберите наилучшее соответствие между шаблонами обычной функции и «базовой» функции
  2. Если на шаге 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*) отмечается специализация, а затем выбирается.