#новичкам
Список постов по теме, Пост про слои
Рассмотрим вариант, когда ParamType - cv-квалифицированная ссылка. Тогда для вывода типа верхний уровень константности аргумента остается только в типе параметра функции, а шаблонный тип выводится без этой константности на верхнем уровне. Любую ссылочность типа аргумента функции мы игнорируем.
template <class T>
void func(const T& param) {...}
// | |
// ParamType
int x = 42;
const int const_x = x;
const int& const_ref_x = x;
std::list<double> lst;
func(x); // T is int, ParamType is const int&
func(const_x); // T is int, ParamType is const int&
func(const_ref_x); // T is int, ParamType is const int&
func(lst); // T is std::list<double>, ParamType is const std::list<double>&
___________________________
template <class T>
void func1(const std::shared_ptr<T>& param) {...}
// | |
// ParamType
std::shared_ptr<double> ptr;
std::shared_ptr<const double> ptr_of_const;
const std::shared_ptr<const double>& const_ref_ptr_of_const;
func1(ptr); // T is double, param's type is const std::shared_ptr<double>&
func1(ptr_of_const); // T is const double, param's type is const std::shared_ptr<const double>&
func1(const_ref_ptr_of_const); // T is const double, param's type is const std::shared_ptr<const double>&
В случае func тип param всегда будет константной ссылкой, вопрос только на что. И это что-то и будет искомым шаблонным типом. И получается он путем отбрасывания константности и ссылочности от типа аргумента функции. Для
const_x откидываем константность, для const_ref_x - и константность и ссылочность. Для x и lst типы выводятся без изменений.Когда у нас в типе param появляется вложенность, то продолжаем обращаться к аналогии с капустой. Просто снимаем весь первый слой со всеми константностями и ссылочностями и оставшееся будет типом Т. На примерах все подписано.
ParamType - cv-квалифицированный указатель
Когда в статьях и книжках расписывают этот вариант, то очень часто отсылаются к такой форме параметра функции const T * param. И говорят, что в этом случае вывод типа шаблонного параметра мало отличается от случая cv-квалифицированных ссылок. И это действительно правда. С одним уточнением, что это указатель на константу, а не константный указатель.
В случае, когда указатель указывает на константный объект - ситуация действительно похожая на ссылки. Отрываем один слой указательности от типа аргумента функции, от того, что осталось убираем константность - получится искомый тип Т.
template <class T>
void func(const T * param) {...}
// | |
// ParamType
int x = 42;
int * p_x = &x; // p_x is a ptr to x as int
int ** p_p_x = &p_x; // p_p_x is a ptr to a ptr to x as int
const int * p_const_x = &x; // p_const_x is a ptr to x as a const int
int * const const_p_x = &x; // const_p_x is a const ptr to x as int
const int * const const_p_const_x = &x; // const_p_const_x is a const ptr to x as const int
func(p_x); // T is int, param's type is const int*
func(p_const_x); // T is int, param's type is const int*
func(const_p_x); // T is int, param's type is const int*
func(const_p_const_x); // T is const int, param's type is const int *
func(p_p_x); // T is int *, param's type is int * const *
Все работает также, только добавляется прикол: если в функцию с такой сигнатурой передать константный указатель, то это не будет учитываться и param будет неконстантным указателем. Здесь как бы создается новый указатель, являющийся копией старого. Он не обязан сам быть константным, так как оригинальный поинтер остается неизменным.
Теперь рассмотрим настоящие константные указатели.