Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 14

Скотт Мейерс

• Если expr представляет собой lvalue, как Т, так и ParamType выводятся как lvalue-ссылки. Это вдвойне необычно. Во-первых, это единственная ситуация в выводе типа шаблона, когда Т выводится как ссылка. Во-вторых, хотя ParamType объявлен с использованием синтаксиса rvalue-ссылки, его выводимым типом является lvalue-ссылка.

• Если expr представляет собой rvalue, применяются “обычные” правила (из случая 1). Примеры

template<typename Т>

void f(T&& param); // param является универсальной ссылкой

int x = 27;        // Как и ранее

const int cx = x;  // Как и ранее

const int& rx = x; // Как и ранее

f(x);              // x - lvalue, так что Т - int&,

                   // тип param также является int&

f(cx);             // cx - lvalue, так что Т - const int&,

                   // тип param также является const int&

f(rx);             // rx - lvalue, так что Т - const int&,

                   // тип param также является const int&

f(27);             // 27 - rvalue, так что Т - int,

                   // следовательно, тип param - int&&

В разделе 5.2 поясняется, почему эти примеры работают именно так, а не иначе. Ключевым моментом является то, что правила вывода типов для параметров, являющихся универсальными ссылками, отличаются от таковых для параметров, являющихся lvalue- или rvalue-ссылками. В частности, когда используются универсальные ссылки, вывод типов различает аргументы, являющиеся lvalue, и аргументы, являющиеся rvalue. Этого никогда не происходит для неуниверсальных ссылок.

Случай 3. ParamType не является ни указателем, ни ссылкой

Когда ParamType не является ни указателем, ни ссылкой, мы имеем дело с передачей по значению:

template<typename Т>

void f(T param); // param передается по значению

Это означает, что param будет копией переданного функции — совершенно новым объектом. Тот факт, что param будет совершенно новым объектом, приводит к правилам, которые регулируют вывод Т из expr.

1. Как и ранее, если типом expr является ссылка, ссылочная часть игнорируется.

2. Если после отбрасывания ссылочной части expr является const, это также игнорируется. Игнорируется и модификатор volatile (объекты volatile являются редкостью и в общем случае используются только при реализации драйверов устройств; детальную информацию на эту тему вы найдете в разделе 7.6.)

Таким образом, получаем следующее:

int x = 27;        // Как и ранее

const int cx = x;  // Как и ранее

const int& rx = x; // Как и ранее

f(x);              // Типами и Т, и param являются int

f(cx);             // Типами и Т, и param вновь являются int

f(rx);             // Типами и Т, и param опять являются int

Обратите внимание, что даже несмотря на то, что cx и rx представляют константные значения, param не является const. Это имеет смысл. param представляет собой объект, который полностью независим от cx и rx, — это копия cx или rx. Тот факт, что cx и rx не могут быть модифицированы, ничего не говорит о том, может ли быть модифицирован param. Вот почему константность expr (как и volatile, если таковой модификатор присутствует) игнорируется при выводе типа param: то, что expr не может быть модифицировано, не означает, что таковой должна быть и его копия.