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