• тип-член type класса remove_reference будет иметь тип string;
• типом возвращаемого значения функции move() будет string&&;
• у параметра t функции move() будет тип string&&;
Соответственно, этот вызов создает экземпляр move, являющийся следующей функцией:
string&& move(string &&t)
Тело этой функции возвращает тип static_cast. Типом t уже является string&&, поэтому приведение не делает ничего. Следовательно, результатом этого вызова будет ссылка на r-значение, которое было дано.
Теперь рассмотрим второе присвоение, которое вызывает функцию std::move(s1). В этом вызове аргументом функции move() является l-значение. Поэтому на сей раз:
• выведенным типом Т будет string& (ссылка на тип string, а не просто string);
• следовательно, экземпляр шаблона remove_reference создается с типом string&;
• тип-член type класса remove_reference будет иметь тип string;
• типом возвращаемого значения функции move() все еще будет string&&;
• параметр t функции move() будет создан как экземпляр string& &&, который сворачивается в string&.
Таким образом, этот вызов создает экземпляр шаблона move, который является точно тем, что необходимо для связи ссылки на r-значение с l-значением.
string&& move(string &t)
Тело этого экземпляра возвращает тип static_cast. В данном случае типом t является string&, который приведение преобразует в тип string&&.
static_cast поддерживает приведение l-значения к ссылке на r-значениеstatic_cast может выполнить только доступные преобразования (см. раздел 16.3). Однако для ссылок на r-значение есть специальное разрешение: даже при том, что нельзя неявно преобразовать l-значение в ссылку на r-значение, используя оператор static_cast, можно
Привязка ссылки на r-значение к l-значению создает код, который работает с разрешением ссылке на r-значение заменять l-значение. Иногда, как в случае с функцией reallocate() класса StrVec (см. раздел 13.6.1), известно, что замена l-значения безопасна.
И наконец, хотя такие приведения можно написать непосредственно, намного проще использовать библиотечную функцию move(). Кроме того, использование функции std::move() существенно облегчает поиск в коде места, потенциально способного заменить l-значения.
Упражнение 16.46. Объясните, что делает этот цикл из функции StrVec::reallocate() (раздел 13.5):
for (size_t i = 0; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++));
Некоторые функции должны перенаправлять другим функциям один или несколько своих аргументов с
В качестве примера напишем функцию, получающую вызываемое выражение и два дополнительных аргумента. Функция вызовет предоставленное вызываемое выражение с другими двумя аргументами в обратном порядке. Вот первый фрагмент функции обращения:
//
//
//
//
template
void flip1(F f, T1 t1, T2 t2) {
f(t2, t1);
}
Этот шаблон работает прекрасно, пока он не используется для вызова функции со ссылочным параметром:
void f(int v1, int &v2) //
{
cout << v1 << " " << ++v2 << endl;
}
Здесь функция f() изменяет значение аргумента, привязанного к параметру v2. Но если происходит вызов функции f() через шаблон flip1, внесенные функцией f() изменения не затронут первоначальный аргумент:
f(42, i); //
flip1(f, j, 42); //