简单的答案是您应该像编写常规引用代码一样为右值引用编写代码,并且您应该在 99% 的时间里以同样的方式对待它们。这包括所有关于返回引用的旧规则(即永远不要返回对局部变量的引用)。
除非您正在编写一个需要利用 std::forward 的模板容器类并且能够编写一个接受左值或右值引用的通用函数,否则这或多或少是正确的。
移动构造函数和移动赋值的一大优点是,如果定义它们,编译器可以在 RVO(返回值优化)和 NRVO(命名返回值优化)调用失败的情况下使用它们。这对于从方法中有效地按值返回昂贵的对象(如容器和字符串)来说是非常巨大的。
现在,右值引用的有趣之处在于,您还可以将它们用作普通函数的参数。这允许您编写具有 const 引用(const foo& other)和右值引用(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 容器已更新为几乎所有内容(哈希键和值、向量插入等)都具有移动重载,并且是您最能看到它们的地方。
您也可以将它们用于普通函数,如果您只提供一个右值引用参数,您可以强制调用者创建对象并让函数执行移动。这与其说是一个很好的用途,不如说是一个示例,但是在我的渲染库中,我为所有加载的资源分配了一个字符串,以便更容易地查看每个对象在调试器中代表什么。界面是这样的:
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);