Читать «Эффективный и современный С++. 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, здесь приводится его сокращенная версия.