返回成员变量时如何利用移动语义?

时间:2013-10-02 09:10:03

标签: c++ c++11 move-semantics

我正在实现一个构建uint8_t向量的工厂类。我希望能够在返回结果向量时使用移动语义。这似乎有效,但我不相信这是完成我想要的正确方法。

我已经看到了很多关于如何将返回的自动变量视为rvalue并使用调用代码的move构造函数的示例,但在我的示例中,返回的对象是成员。我知道如果调用者将返回值放入移动构造函数中,该成员将丢失其内容 - 这正是我想要的。

我写的是这样的:

#include <cstdint>
#include <iostream>
#include <vector>

class Factory
{
public:
    std::vector<uint8_t> _data;

    Factory(std::size_t size) :
        _data(size, 0)
    {
    }

    void buildContent(int param)
    {
        // perform operations on the contents of _data
    }

    std::vector<uint8_t> && data()
    {
        return std::move(_data);
    }
};

int main()
{
    Factory factory(42);
    factory.buildContent(1);
    std::vector<uint8_t> temp(factory.data());
    std::cout << "temp has " << temp.size() << " elements" << std::endl;
    std::cout << "factory._data has " << factory._data.size() << " elements" << std::endl;

    return 0;
}

修改

哦,示例代码输出以下内容:

temp has 42 elements
factory._data has 0 elements

3 个答案:

答案 0 :(得分:6)

首先,你必须决定你想要什么。你真的想从你的实例中吸取数据吗?如果是这样,你所取得的成就就是那么好。另一方面,它看起来很不安全。您可以做的是使用引用限定符来强制您只在实例本身是右值引用时返回右值引用:

std::vector<uint8_t> && data() && // called on rvalue
{
    return std::move(_data);
}

// return lvalue ref or value
std::vector<uint8_t>& data() & // called on lvalue
{
    return _data;
}

最后,这一切都取决于你要解决的问题,这在你的问题中并不明显。

答案 1 :(得分:1)

如果您的编译器具有对此的rvalue引用(方法后为&&),您可能希望使用它。见@ juanchopanza的回答。

如果您不这样做,首先要确保data()明确表示您正在移动。有几种方法可以做到这一点。

首先,非成员方法(friend s)可以覆盖&&。所以你可以得到这样的语法:

std::vector<uint8_t> temp(get_data( std::move(factory) );

get_data &&类型的& factorystd::vector<uint8_t>重载,并且基于它移动或不移动。

接下来,您希望返回`std::vector<uint8_t>&&而不是create_factory的生命周期延期问题。运行时成本介于零和小之间,但消除的错误是值得的。

如果Factory返回for( uint8_t x : get_data( create_factory() ) ) 个对象,那么如果我们这样做:

get_data

返回&&的{​​{1}}不起作用,而返回临时工作的create_factory完美无缺。

发生了什么事?好吧,基于范围的for循环被定义为将您正在迭代的东西绑定到引用。与引用的临时绑定的生命周期已延长:绑定到引用的引用没有生命周期扩展。在任何一种情况下,&&函数的返回值的生命周期都不会延长。

对于factory情况,对矢量的引用是悬空的。在返回临时的情况下,&&的向量移动到临时,然后延长临时寿命。

简而言之,返回{{1}}引用很少是一个好主意。

答案 2 :(得分:0)

如何将赋值运算符隐藏在向量中并仅实现移动赋值运算符(基本上不允许复制 - 如果这是你想要的那样)。

这样:

std::vector<uint8_t> temp(factory.data());

除非您将其更改为:

,否则不会编译
std::vector<uint8_t> temp(std::move(factory));