Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 13
Скотт Мейерс
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<typename Т>
void f(const T& param); // param является ссылкой на const
int x = 27; // Как и ранее
const int cx = x; // Как и ранее
const int& rx = x; // Как и ранее
f(x); // Т - int, тип param - const int&
f(cx); // Т - int, тип param - const int&
f(rx); // Т - int, тип param - const int&
Как и ранее, “ссылочность” rx
при выводе типа игнорируется.
Если бы param
был указателем (или указателем на const
), а не ссылкой, все бы работало, по сути, точно так же:
template<typename Т>
void f(T* param); // Теперь param является указателем
int x = 27; // Как и ранее
const int *px = &x; // px - указатель на x, как на const int
f(&x); // Т - int, тип param - int*
f(px); // Т - const int, тип param - const int*
Сейчас вы можете обнаружить, что давно усердно зеваете, потому что все это очень скучно, правила вывода типов в С++ работают так естественно для ссылок и указателей, что все просто очевидно! Это именно то, что вы хотите от системы вывода типов.
Случай 2. ParamType
является универсальной ссылкой
Все становится менее очевидным в случае шаблонов, принимающих параметры, являющиеся универсальными ссылками. Такие параметры объявляются как ссылки rvalue (т.е. в шаблоне функции, принимающем параметр типа Т
, объявленным типом универсальной ссылки является Т&&
), но ведут себя иначе при передаче аргументов, являющихся lvalue. Полностью вопрос рассматривается в разделе 5.2, здесь приводится его сокращенная версия.