Проста відповідь полягає в тому, що ви повинні написати код для посилань на rvalue, як і звичайний код посилань, і ви повинні ставитись до них однаково подумки, 99% часу. Сюди входять всі старі правила повернення посилань (тобто ніколи не повертайте посилання на локальну змінну).
Якщо ви не пишете клас контейнерів шаблонів, який повинен скористатися перевагою std :: forward і вміти записати загальну функцію, яка приймає або посилання lvalue, або rvalue, це більш-менш вірно.
Однією з головних переваг конструктора переміщення та призначення переміщення є те, що якщо ви їх визначите, компілятор може використовувати їх у випадках, коли не вдалося викликати RVO (оптимізація зворотного значення) та NRVO (названа оптимізація повернення). Це досить величезно для ефективного повернення дорогих об'єктів, таких як контейнери та рядки, на ефективність.
Тепер, коли речі стають цікавими з посиланнями на rvalue, це те, що ви також можете використовувати їх як аргументи до нормальних функцій. Це дозволяє писати контейнери, які мають перевантаження як для посилання const (const foo & other), так і для посилання rvalue (foo && other). Навіть якщо аргумент є занадто непростим для передачі за допомогою простого виклику конструктора, все одно можна зробити:
std::vector vec;
for(int x=0; x<10; ++x)
{
// automatically uses rvalue reference constructor if available
// because MyCheapType is an unamed temporary variable
vec.push_back(MyCheapType(0.f));
}
std::vector vec;
for(int x=0; x<10; ++x)
{
MyExpensiveType temp(1.0, 3.0);
temp.initSomeOtherFields(malloc(5000));
// old way, passed via const reference, expensive copy
vec.push_back(temp);
// new way, passed via rvalue reference, cheap move
// just don't use temp again, not difficult in a loop like this though . . .
vec.push_back(std::move(temp));
}
Контейнери STL були оновлені, щоб перевантажувати переміщення майже нічого (хеш-ключ і значення, вставка вектора тощо), і саме там ви їх побачите найбільше.
Ви також можете використовувати їх для звичайних функцій, і якщо ви надаєте лише довідковий аргумент rvalue, ви можете змусити абонента створити об'єкт і дозволити функції рухатися. Це скоріше приклад, ніж дійсно вдале використання, але в моїй бібліотеці візуалізації я призначив рядок для всіх завантажених ресурсів, щоб було легше побачити, що кожен об’єкт представляє у відладчику. Інтерфейс приблизно такий:
TextureHandle CreateTexture(int width, int height, ETextureFormat fmt, string&& friendlyName)
{
std::unique_ptr<TextureObject> tex = D3DCreateTexture(width, height, fmt);
tex->friendlyName = std::move(friendlyName);
return tex;
}
Це форма "протікаючої абстракції", але дозволяє мені скористатися тим, що мені довелося створювати рядок вже більшу частину часу, і уникати чергового його копіювання. Це не зовсім високоефективний код, але є хорошим прикладом можливостей, коли люди отримують цю функцію. Цей код фактично вимагає, щоб змінна або була тимчасовою для виклику, або std :: move викликається:
// move from temporary
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, string("Checkerboard"));
або
// explicit move (not going to use the variable 'str' after the create call)
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, std::move(str));
або
// explicitly make a copy and pass the temporary of the copy down
// since we need to use str again for some reason
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, string(str));
але це не складеться!
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, str);