C ++ 11 rvalues和移动语义混淆(return语句)

我试图理解右值引用并移动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();

第二个例子

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();

第三个例子

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();

第一个例子

std::vector<int> return_vector(void)
{
    std::vector<int> tmp {1,2,3,4,5};
    return tmp;
}

std::vector<int> &&rval_ref = return_vector();

第一个示例返回由rval_ref捕获的临时rval_ref 。 这个临时的生命将延伸到rval_ref定义之外,你可以像使用它的价值一样使用它。 这与以下内容非常相似:

const std::vector<int>& rval_ref = return_vector();

除了在我的重写中,显然不能以非const方式使用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();

在第二个示例中,您创建了运行时错误。 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();

你的第三个例子大致相当于你的第一个例子。 tmp上的std :: move是不必要的,实际上可能是一种性能悲观,因为它会阻止返回值优化。

编码你在做什么的最好方法是:

最佳实践

std::vector<int> return_vector(void)
{
    std::vector<int> tmp {1,2,3,4,5};
    return tmp;
}

std::vector<int> rval_ref = return_vector();

就像你在C ++ 03中一样。 在返回语句中,tmp被隐式视为右值。 它将通过返回值优化(不复制,不移动)返回,或者如果编译器决定它不能执行RVO,那么它将使用向量的移动构造函数执行返回。 只有在RVO未执行且返回的类型没有移动构造函数的情况下,复制构造函数才会用于返回。


他们中的任何一个都不会复制,但第二个将会引用一个被毁坏的向量。 命名的右值引用在常规代码中几乎不存在。 你只会写下你如何在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();

除了现在,矢量被移动。 在绝大多数情况下,类的用户不会处理它的右值引用。


简单的答案就是你应该为你的常量引用代码编写右值引用代码,并且你应该在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);
链接地址: http://www.djcxy.com/p/73157.html

上一篇: C++11 rvalues and move semantics confusion (return statement)

下一篇: c++