С другой стороны, когда происходит вызов функции f3() для l-значения i, типом T будет int&. Когда определяется и инициализируется локальная переменная t, у нее будет тип int&. Инициализация переменной t свяжет ее с параметром val. При присвоении переменной t одновременно изменяется и параметр val. В этом экземпляре функции f3() оператор if всегда будет возвращать значение true.
На удивление сложно написать правильный код, когда задействованные типы могут быть простыми (не ссылочными) типами или ссылочными типами (хотя такие классы трансформации типов, как remove_reference (см. раздел 16.2.3), вполне могут помочь в этом).
На практике параметры в виде ссылки на r-значение используются в одном из двух случаев: либо когда шаблон перенаправляет свои аргументы, ли когда шаблон перегружается. Перенаправление рассматривается в разделе 16.2.7, а перегрузка шаблона в разделе 16.3, а пока достаточно знать, что стоит обратить внимание на то, что шаблоны функций, использующие ссылки на r-значение, зачастую используют перегрузку таким же образом, как описано в разделе 13.6.3:
template
//
template
//
Подобно нешаблонным функциям, первая версия будет связана с изменяемым r-значением, а вторая с l-значением или константным r-значением.
Упражнение 16.42. Определите типы Т и val в каждом из следующих вызовов:
template
int i = 0; const int ci = i;
(a) g(i); (b) g(ci); (c) g(i * ci);
Упражнение 16.43. Используя определенную в предыдущем упражнении функцию, укажите, каким будет параметр шаблона g() при вызове g(i = ci)?
Упражнение 16.44. Используя те же три вызова, что и в первом упражнении, определите типы T, если параметр функции g() объявляется как T (а не Т&&) и как const Т&?
Упражнение 16.45. С учетом следующего шаблона объясните происходящее при вызове функции g() с таким литеральным значением, как 42, и с переменной типа int?
template
std::move()
Библиотечная функция move() (см. раздел 13.6.1) — хороший пример шаблона, использующего ссылки на r-значение. К счастью, функцию move() можно использовать, не понимая механизма работы используемого ею шаблона. Однако изучение работы функции move() может помочь понять и использовать шаблоны.
В разделе 13.6.2 обращалось внимание на то, что, хотя и нельзя непосредственно привязать ссылку на r-значение к l-значению, функцию move() можно использовать для получения ссылки на r-значение, связанной с l-значением. Поскольку функция move() может получать аргументы, по существу, любого типа, нет ничего удивительного в том, что move() — это шаблон функции.
std::move()Стандартное определение функции move() таково:
//
//
//
template
typename remove_reference
//
return static_cast
}
Этот код короток, но сложен. В первую очередь, параметр функции move(), Т&& является ссылкой на r-значение типа параметра шаблона. Благодаря сворачиванию ссылок этот параметр может соответствовать аргументу любого типа. В частности, функции move() можно передать либо l-, либо r-значение:
string s1("hi!"), s2;
s2 = std::move(string("bye!")); //
s2 = std::move(s1); //
//
std::move()В первом присвоении аргумент функции move() является r-значением, полученным в результате выполнения конструктора string("bye") класса string. Как уже упоминалось, при передаче r-значения ссылочному r-значению параметра функции выведенный из этого аргумента тип является ссылочным типом (см. раздел 16.2.5). Таким образом, в вызове std::move(string("bye!")):
• выведенным типом T будет string;
• следовательно, экземпляр шаблона remove_reference создается с типом string;