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



Случай 2. >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. Этого никогда не происходит для неуниверсальных ссылок.

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

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

>template

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

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

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