移动和转发案例使用

时间:2015-09-02 09:51:34

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

我跟着this tutorial开始理解C ++ 11中的移动语义和右值引用。 在某些时候,他使用移动构造函数中的std::move实现了这两个类,并解释了

  

我们将临时文件传递给一个移动构造函数,它会带来新的生命   在新范围内。在rvalue表达式的上下文中   评估后,临时对象真的结束了。但在   我们的构造函数,对象有一个名字;它会活着的   我们功能的整个持续时间。换句话说,我们可能会使用   函数中不止一次变量,而临时变量   对象有一个确定的位置,真正坚持整个   功能。它是真正意义上的定位器值

的左值
class MetaData
{
public:
    MetaData(int size, const string& name)
        : _name(name)
        , _size(size)
    {}

    MetaData(const MetaData& other)
        : _name(other._name)
        , _size(other._size)
    {
        cout << "MetaData -- Copy Constructor" << endl;
    }

    MetaData(MetaData&& other)
        : _name(move(other._name))
        , _size(other._size)
    {
        cout << "MetaData -- Move Constructor" << endl;
    }

  ~MetaData()
  {
    _name.clear();
  }

    string getName() const { return _name; }
    int getSize() const { return _size; }

private:
    string _name;
    int _size;
};

class ArrayWrapper
{
public:
    ArrayWrapper()
        : _p_vals(new int[64])
        , _metadata(64, "ArrayWrapper")
    {}

    ArrayWrapper(int n)
        : _p_vals(new int[n])
        , _metadata(n, "ArrayWrapper")
    {}

    ArrayWrapper(ArrayWrapper&& other)
        : _p_vals(other._p_vals)
        , _metadata(move(other._metadata))
    {
        cout << "ArrayWrapper -- Move Constructor" << endl;
        other._p_vals = nullptr;
    }

    ArrayWrapper(const ArrayWrapper& other)
        : _p_vals(new int[other._metadata.getSize()])
        , _metadata(other._metadata)
    {
        cout << "ArrayWrapper -- Copy Constructor" << endl;
        for (int i = 0; i < _metadata.getSize(); ++i)
            _p_vals[i] = other._p_vals[i];
    }

    ~ArrayWrapper()
    {
        delete[] _p_vals;
    }

    int* getVals() const { return _p_vals; }
    MetaData getMeta() const { return _metadata; }

private:
    int* _p_vals;
    MetaData _metadata;
};

在ArrayWrapper移动构造函数中,我尝试用std::move更改std::forward<MetaData>,代码显示如果我调用ArrayWrapper移动构造函数,这将调用MetaData移动构造函数,就像{{{}的示例一样1}}。

当然,如果我不使用std::movestd::move,将调用MetaData副本构造函数。

问题是,在这种情况下,使用std::forwardstd::move之间有区别吗?我为什么要用一个而不是另一个?

2 个答案:

答案 0 :(得分:3)

  

使用std :: move和std :: forward之间有区别吗?我为什么要用一个而不是另一个?

是的,std::move会返回其参数的右值引用,而std::forward只会转发保留其值类别的参数。

当您明确想要将某些内容转换为右值时,请使用move。如果您不知道自己(可能是左值或左值)并希望将其完美转发(保留其l或r值),请使用forwardCan I typically/always use std::forward instead of std::move?是您可能感兴趣的问题。

在下面的代码段中,bar将完全获得foo的调用者已经过去的内容,包括保留的值类别:

template <class T>
void foo(T&& t) {
    bar(std::forward<T>(t));
}

不要让T&&欺骗你 - t is not an rvalue reference。当它出现在类型推导上下文中时,T&&获得了特殊含义。实例化foo时,T取决于传递的参数是左值还是右值。如果它是U类型的左值,则T会推断为U&。如果它是右值,T会推断为U。有关详细信息,请参阅this excellent article。您需要了解value categoriesreference collapsing,以便在这方面更好地了解事情。

答案 1 :(得分:1)

相关的std::forwardstd::move声明为:

template< class T >
T&& forward( typename std::remove_reference<T>::type& t );

template< class T >
typename std::remove_reference<T>::type&& move( T&& t );

对于前者:

std::forward<MetaData>(other._metadata);

std::forward<MetaData>返回MetaData&&

对于后者:

 std::move(other._metadata);
 //argument derived as lvalue reference due to forwarding reference
 std::move<MetaData&>(other._name);

std::move<MetaData&>返回typename std::remove_reference<MetaData&>::type&&,即MetaData&&

因此,两个表单对于您的示例是相同的。但是,std::move是正确的选择,因为它显示了我们无条件地移动参数的意图。 std::forward 可以无条件地移动,但它的目的是完善它的论点。