пятница, 7 сентября 2018 г.

Перегрузка и частичная специализация функций

Возьмем следующий пример кода:
template <typename T> void foo(T); // строка 1
template <typename T> void foo(T*); // строка 2
template <>           void foo<int>(int*); // строка 3
Здесь в строке 1 мы объявляем шаблон функции с одним параметром T.
В строке 2 перегружаем нашу шаблонную функцию.
В строке 3 выполняем полную специализацию шаблонной функции, а именно ее перегруженного варианта в строке 2.

Теперь попробуем переставить строки 2 и 3 местами:
template <typename T> void foo(T); // строка 1
template <>           void foo<int*>(int*); // строка 2
template <typename T> void foo(T*); // строка 3
В обоих случаях будем вызывать нашу функцию так:
foo(new int);
Вопреки ожиданиям столь незначительное изменение меняет поведение программы.
Дело в том, что теперь специализация шаблонной функции в строке 2 относится к шаблонной функции в строке 1.

Далее в игру вступает та особенность компилятора, при которой он сначала выбирает наиболее подходящую перегрузку функции, а затем для данной перегрузки еще выбирает специализацию.
В первом фрагменте кода, компилятор сначала выбирает подходящую перегрузку функции, что для new int очевидно соответствует строке 2, а затем выбирает подходящую специализацию, и в итоге вызывается функция из строки 3:
template <>           void foo<int>(int*); // строка 3
Во втором фрагменте кода, компилятор всё также сначала выбирает перегрузку функции, это строка 3, потом смотрит, если ли специализации, но специализаций у данной функции нет. Во втором фрагменте кода специализация есть только у функции из строки 1, а у функции из строки 3 нет. В итоге вызывается функция из строки 3:
template <typename T> void foo(T*); // строка 3
По это причине не рекомендуется одновременно использовать перегрузки функций и их полную специализацию.

Комментариев нет:

Отправить комментарий