Tar*_*ula 415 c++ c++-faq rvalue-reference move-semantics c++11
我试图理解rvalue引用并移动C++ 11的语义.
这些示例之间有什么区别,哪些不会执行矢量复制?
std::vector<int> return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return tmp;
}
std::vector<int> &&rval_ref = return_vector();
Run Code Online (Sandbox Code Playgroud)
std::vector<int>&& return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return std::move(tmp);
}
std::vector<int> &&rval_ref = return_vector();
Run Code Online (Sandbox Code Playgroud)
std::vector<int> return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return std::move(tmp);
}
std::vector<int> &&rval_ref = return_vector();
Run Code Online (Sandbox Code Playgroud)
How*_*ant 543
std::vector<int> return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return tmp;
}
std::vector<int> &&rval_ref = return_vector();
Run Code Online (Sandbox Code Playgroud)
第一个示例返回一个临时的,它被捕获rval_ref.那个临时的生命将超出rval_ref定义,你可以使用它,好像你已经按价值捕获它.这非常类似于以下内容:
const std::vector<int>& rval_ref = return_vector();
Run Code Online (Sandbox Code Playgroud)
除了在我的重写中,你显然不能rval_ref以非常规的方式使用.
std::vector<int>&& return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return std::move(tmp);
}
std::vector<int> &&rval_ref = return_vector();
Run Code Online (Sandbox Code Playgroud)
在第二个示例中,您创建了一个运行时错误. rval_ref现在持有对函数tmp内部被破坏的引用.运气好的话,这段代码会立即崩溃.
std::vector<int> return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return std::move(tmp);
}
std::vector<int> &&rval_ref = return_vector();
Run Code Online (Sandbox Code Playgroud)
你的第三个例子大致相当于你的第一个例子.该std::move上tmp是不必要的,而且实际上是一个性能pessimization,因为它会抑制返回值优化.
编码您正在做的事情的最佳方式是:
std::vector<int> return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return tmp;
}
std::vector<int> rval_ref = return_vector();
Run Code Online (Sandbox Code Playgroud)
就像你在C++ 03中一样. tmp在return语句中隐式地将其视为rvalue.它将通过返回值优化(无复制,无移动)返回,或者如果编译器决定它不能执行RVO,那么它将使用向量的移动构造函数来执行返回.仅当未执行RVO时,并且如果返回的类型没有移动构造函数,则复制构造函数将用于返回.
Pup*_*ppy 41
它们都不会复制,但第二个会引用一个被破坏的矢量.命名的右值引用几乎从不存在于常规代码中.你只需要在C++ 03中编写一个副本就可以了.
std::vector<int> return_vector()
{
std::vector<int> tmp {1,2,3,4,5};
return tmp;
}
std::vector<int> rval_ref = return_vector();
Run Code Online (Sandbox Code Playgroud)
除了现在,矢量被移动.在绝大多数情况下,类的用户不处理它的右值引用.
Zon*_*ner 16
简单的答案是你应该为rvalue引用编写代码,就像你经常引用代码一样,你应该在99%的时间里对它们进行相同的处理.这包括有关返回引用的所有旧规则(即永远不会返回对局部变量的引用).
除非您正在编写需要利用std :: forward并且能够编写采用左值或右值引用的泛型函数的模板容器类,否则这或多或少都是正确的.
移动构造函数和移动赋值的一个重要优点是,如果定义它们,编译器可以在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));
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
它是一种"漏洞抽象"的形式,但允许我利用我必须在大多数时间创建字符串的事实,并避免再次复制它.这不是完全高性能的代码,但是当人们掌握这个功能时,这是一个很好的例子.这段代码实际上要求变量是调用的临时变量,或调用std :: move:
// move from temporary
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, string("Checkerboard"));
Run Code Online (Sandbox Code Playgroud)
要么
// 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));
Run Code Online (Sandbox Code Playgroud)
要么
// 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));
Run Code Online (Sandbox Code Playgroud)
但这不会编译!
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, str);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
97052 次 |
| 最近记录: |