• тип-член 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); //