Эффективный и современный С++. 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

Случай 1. >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&