#опытным
Во многих образовательных ресурсах на русском и английском языке я видел, как рассказывают про частичную специализацию шаблонов функций. Ресурсы конечно не очень авторитетные, но если читать про шаблоны в стандарте, то голова вспухнет. Давайте сейчас проясним этот момент.
Частичной специализации шаблонов функции не существует. И точка!
То, что называют выдают за нее - это обычная перегрузка шаблонных функций.
Звучит, как пустяковая проблема. Какая разница, как назвать молоток, если им все равно можно забить гвоздь?
Безусловно, вы правы. Большинству разработчиков такие тонкости знать не нужно. Но мы ведь тут грокаем С++, у нас много постов про такие тонкости. Поэтому погнали.
Шаблонные функции, как и обычные функции, можно перегружать. Логично было принести этот функционал в шаблоны, ибо перегрузки могут быть вообще не связаны ни одним общим параметром.
void f(int) { std::cout << "int-overload" << std::endl; };
void f(int*){ std::cout << "int-p-overload" << std::endl; }
template<class T> void f(T) { std::cout << "T-overload" << std::endl; };
template<class T> void f(T*){ std::cout << "T-p-overload" << std::endl; }
Заметьте, что синтаксис одинаковый с точностью до появления template<class T> и замены конкретного типа на шаблонный параметр.
Частичная специализация же характерна только для шаблонов классов и переменных.
template<typename T>
class Foo {};
template<typename T>
class Foo<T*> {};
И в специализированном классе мы должны показать с помощью треугольных скобок, для какой подгруппы типов мы специализируем шаблон. И это обязательно. Специализация должна показать, что она именно специализация какого-то общего шаблона. Ведь перегрузок шаблонов классов не бывает:
template <typename T1>
struct Foo<T1> {};
// Так нельзя делать, это несвязанные шаблоны
template <typename T1, typename T2>
struct Foo<T1,T2> {};
С этим разобрались. Давайте пофантазирует, что будет, если бы мы могли частично специализировать шаблон функции, при этом оставили бы дефолтный механизм перегрузки:
template<class T> void f(T) { std::cout << "T-overload" << std::endl; };
template<class T> void f(T*){ std::cout << "T-p-overload" << std::endl; }
template<class T> void f<T*>(T*){std::cout << "T-p-specialization" << std::endl;}Ну и как компилятору выбирать между T-p-overload и T-p-specialization?
Частичная специализация шаблонов функций вводила бы неоднозначность или дополнительную сложность в порядок разрешения перегрузок функции. Да и перегрузка полностью покрывает функциональность частичной специализации. Поэтому ее не существует.
На самом деле без понимания того, какими способами можно изменить поведение шаблона, нельзя нормально понять, как компилятор выбирает правильного кандидата для вызова. Это тонкости, но на высоком уровне тонкости все больше и больше роляют.
Don't be confused. Stay cool.
#template #cppcore