简单地说,您应该像编写常规引用代码一样编写 rvalue 引用代码,并且在 99% 的情况下应该将它们视为相同。这包括有关返回引用的所有旧规则(即永远不要返回对局部变量的引用)。
除非您正在编写一个需要利用 std::forward 并能够编写接受左值或右值引用的通用函数的模板容器类,否则这是更为真实的。
移动构造函数和移动赋值的一个巨大优势在于,如果你定义了它们,编译器可以在 RVO(返回值优化)和 NRVO(命名返回值优化)无法调用时使用它们。这对于从方法有效地按值返回昂贵的对象,如容器和字符串,来说非常重要。
现在,使用 rvalue 引用作为普通函数的参数也很有趣。这使您可以编写具有 const 引用(const foo &other)和 rvalue 引用(foo && other)的重载容器。即使参数过于复杂以至于不能通过纯构造函数调用传递,仍然可以这样做:
std::vector vec;
for(int x=0; x<10; ++x)
{
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));
vec.push_back(temp);
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);
std::move(expression)
并不会创建新的对象,它只是将表达式转换为一个 xvalue。在计算std::move(expression)
的过程中没有任何对象被复制或移动。 - fredoverflow