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