11

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

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

我写了这样的东西:

#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
4

3 回答 3

8

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

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;
}

最后,这完全取决于您要解决的问题,这在您的问题中并不明显。

于 2013-10-02T09:35:17.337 回答
2

如果您的编译器有对 this 的右值引用(&&after 方法),您可能想要使用它。请参阅@juanchopanza 的回答。

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

首先,非成员方法(friends)可以覆盖 on &&。所以你可以得到这个语法:

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

where get_datahas&&&重载您的factory类型,并且基于它移动或不移动。

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

如果create_factory返回一个Factory对象,那么如果我们这样做:

for( uint8_t x : get_data( create_factory() ) )

get_data返回 a的那个&&不起作用,而返回一个临时的则完美无缺。

到底是怎么回事?好吧,基于范围的 for 循环被定义为将您正在迭代的内容绑定到引用。临时绑定到引用的生命周期延长:绑定到引用的引用没有生命周期延长。在任何一种情况下,create_factory函数的返回值的生命周期都不会延长。

在这种&&情况下,对向量的引用是悬空的。在 return-temporary 的情况下,factory' 向量被移动到临时的,然后临时的生命周期被延长。

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

于 2013-10-02T15:33:58.983 回答
0

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

这样:

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

除非您将其更改为此,否则不会编译:

std::vector<uint8_t> temp(std::move(factory));
于 2013-10-04T11:03:06.490 回答