Простой ответ заключается в том, что вы должны написать код для rvalue ссылок, как если бы вы использовали обычный код ссылок, и вы должны обращаться с ними одинаково мысленно 99% времени. Это включает в себя все старые правила возврата ссылок (т.е. никогда не возвращать ссылку на локальную переменную).
Если вы не пишете класс контейнера шаблона, который должен использовать преимущества std :: forward и иметь возможность написать универсальную функцию, которая принимает ссылки либо на lvalue, либо на rvalue, это более или менее верно.
Одним из больших преимуществ конструктора перемещения и назначения перемещения является то, что если вы определите их, компилятор может использовать их в тех случаях, когда RVO (оптимизация возвращаемого значения) и NRVO (именованная оптимизация возвращаемого значения) не будут вызваны. Это довольно много для эффективного возврата дорогостоящих объектов, таких как контейнеры и строки, из методов.
Теперь, когда вещи становятся интересными со ссылками на rvalue, вы можете также использовать их в качестве аргументов обычных функций. Это позволяет вам писать контейнеры с перегрузками как для константной ссылки (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);