移动和转发案例使用

Move and Forward cases use

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

we pass the temporary to a move constructor, and it takes on new life in the new scope. In the context where the rvalue expression was evaluated, the temporary object really is over and done with. But in our constructor, the object has a name; it will be alive for the entire duration of our function. In other words, we might use the variable other more than once in the function, and the temporary object has a defined location that truly persists for the entire function. It's an lvalue in the true sense of the term locator value

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 移动构造函数,就像示例中的std::move.

当然,如果我不使用 std::movestd::forward,将调用 MetaData 复制构造函数。

问题是,在这种情况下,使用std::movestd::forward有区别吗?为什么我应该使用一个而不是另一个?

is there a difference between using std::move and std::forward? Why should I use one instead of the other?

是的,std::move returns 其参数的右值引用,而 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. When it appears in a type-deducing context, T&& acquires a special meaning. When foo is instantiated, T depends on whether the argument passed is an lvalue or an rvalue. If it's an lvalue of type U, T is deduced to U&. If it's an rvalue, T is deduced to U. See this excellent article for details. You need to understand about value categories and reference collapsing 可以更好地理解这方面的事情。

相关的 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> returns MetaData&&.

对于后者:

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

std::move<MetaData&>returnstypename std::remove_reference<MetaData&>::type&&,也就是MetaData&&.

因此,对于您的示例,这两种形式是相同的。然而,std::move 在这里是正确的选择,因为它显示了我们无条件移动参数的意图。 std::forward可以用来无条件移动,但目的是完美转发其参数