Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - страница 9
. Этот раздел содержит информацию, которую вы должны знать.
Если вы готовы посмотреть сквозь пальцы на применение небольшого количества псевдокода, то можно рассматривать шаблон функции как имеющий следующий вид:
>template
>void(ParamType param);
Вызов может выглядеть следующим образом:
>f(expr); // Вызов f с некоторым выражением
В процессе компиляции компилятор использует expr для вывода двух типов: типа >Т
и типа >ParamType
. Эти типы зачастую различны, поскольку >ParamType
часто содержит “украшения”, например >const
или квалификаторы ссылки. Например, если шаблон объявлен как
>template
>void f(const T& param); // ParamType - const T&
и мы осуществляем вызов
>int x = 0;
>f(x); // Вызов f с параметром int
то >Т
выводится как >int
, а >ParamType
— как >const int&
.
Вполне естественно ожидать, что тип, выведенный для >Т
, тот же, что и тип переданного функции аргумента, т.е. что >Т
— это тип выражения >expr
. В приведенном выше примере это так: >x
— значение типа >int
и >Т
выводится как >int
. Но вывод не всегда работает таким образом. Тип, выведенный для >T
, зависит не только от типа >expr
, но и от вида >ParamType
. Существует три случая.
• >ParamType
представляет собой указатель или ссылку, но не универсальную ссылку. (Универсальные ссылки рассматриваются в разделе 5.2. Пока что все, что вам надо знать, — что они существуют и не являются ни ссылками lvalue, ни ссылками rvalue.)
• >ParamType
является универсальной ссылкой.
• >ParamType
не является ни указателем, ни ссылкой.
Следовательно, нам надо рассмотреть три сценария вывода. Каждый из них основан на нашем общем виде шаблонов и их вызова:
>template
>void f(ParamType param);
>f(expr); // Вывод Т и ParamType из expr
>ParamType
является указателем или ссылкой, но не универсальной ссылкойПростейшая ситуация — когда >ParamType
является ссылочным типом или типом указателя, но не универсальной ссылкой. В этом случае вывод типа работает следующим образом.
1. Если типом >expr
является ссылка, ссылочная часть игнорируется.
2. Затем выполняется сопоставление типа >expr
с >ParamType
для определения >Т
. Например, если у нас имеются шаблон
>template
>void f(T& param); // param представляет собой ссылку
и объявления переменных
>int x = 27; // x имеет тип int
>const int cx = x; // cx имеет тип const int
>const int& rx = x; // rx является ссылкой на x как на const int
то выводимые типы для >param
и >Т
в различных выводах будут следующими:
>f(x); // Т - int, тип param - int&
>f(cx); // Т - const int, тип param - const int&
>f(rx); // Т - const int, тип param - const int&
Во втором и третьем вызовах обратите внимание, что, поскольку >cx
и >rx
объявлены как константные значения, >Т
выводится как >const int
тем самым приводя к типу параметра >const int&
. Это важно для вызывающего кода. Передавая константный объект параметру-ссылке, он ожидает, что объект останется неизменным, т.е. что параметр будет представлять собой ссылку на >const
. Вот почему передача константного объекта в шаблон, получающий параметр >T&
, безопасна: константность объекта становится частью выведенного для >Т
типа.
В третьем примере обратите внимание, что несмотря на то, что типом >rx
является ссылка, тип >T
выводится как не ссылочный. Вот почему при выводе типа игнорируется “ссылочность” >rx
.
Все эти примеры показывают ссылочные параметры, являющиеся lvalue, но вывод типа точно так же работает и для ссылочных параметров rvalue. Конечно, аргументы могут передаваться только ссылочным параметрам, являющимся rvalue, но это ограничение никак не влияет на вывод типов.
Если мы изменим тип параметра >f
с >Т&
на >const Т&
, произойдут небольшие изменения, но ничего удивительного не случится. Константность >cx
и >rx
продолжает соблюдаться, но поскольку теперь мы считаем, что >param
является ссылкой на >const
, >const
как часть выводимого типа >T
не требуется:
>template
>void f(const T& param); // param является ссылкой на const
>int x = 27; // Как и ранее
>const int cx = x; // Как и ранее
>const int& rx = x; // Как и ранее
>f(x); // Т - int, тип param - const int&