为什么需要std :: move?

Per*_*est 11 c++ c++11

我在C++ 11中阅读了一篇关于移动语义的精彩文章.本文以非常直观的方式编写.下面给出了本文中的示例类.

class ArrayWrapper 
{ 
public: 
    // default constructor produces a moderately sized array 
    ArrayWrapper () 
        : _p_vals( new int[ 64 ] ) 
        , _metadata( 64, "ArrayWrapper" ) 
    {} 

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

    // move constructor 
    ArrayWrapper (ArrayWrapper&& other) 
        : _p_vals( other._p_vals  ) 
        , _metadata( other._metadata ) 
    { 
        other._p_vals = NULL; 
    } 

    // copy constructor 
    ArrayWrapper (const ArrayWrapper& other) 
        : _p_vals( new int[ other._metadata.getSize() ] ) 
        , _metadata( other._metadata ) 
    { 
        for ( int i = 0; i < _metadata.getSize(); ++i ) 
        { 
            _p_vals[ i ] = other._p_vals[ i ]; 
        } 
    } 
    ~ArrayWrapper () 
    { 
        delete [] _p_vals; 
    } 
private: 
    int *_p_vals; 
    MetaData _metadata; 
};
Run Code Online (Sandbox Code Playgroud)

显然,在上面的移动构造函数实现中,嵌入元素不会发生移动_metadata.为了方便这一点,诀窍是使用这样的std::move()方法.

ArrayWrapper (ArrayWrapper&& other) 
        : _p_vals( other._p_vals  ) 
        , _metadata( std::move( other._metadata ) ) 
{ 
    other._p_vals = NULL; 
} 
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.

标准说:

§5(C++11§5[expr]/6):

[注意:表达式是xvalue,如果它是:

  • 调用函数的结果,无论是隐式还是显式,其返回类型是对象类型的右值引用,

  • 强制转换为对象类型的右值引用,

  • 一个类成员访问表达式,指定非引用类型的非静态数据成员,其中对象表达式是xvalue,或者

  • 一个.*指针到构件表达,其中第一操作数是一个x值和第二个操作数是一个指向数据成员.

我的问题:

现在,other移动构造函数中的变量是一个xvalue(我是对的吗?).然后根据上面的最后一条规则,other._metadata也应该是一个xvalue.因此编译器可以隐含地使用类的移动构造函数_metadata.所以,不需要在std::move这里.

我错过了什么?

Dav*_*eas 15

你的假设不是真的.构造函数的参数是一个xvalue,它允许绑定rvalue-reference,但是一旦绑定了rvalue-reference,在构造函数中,它就不再是一个xvalue而是一个lvalue.从概念上讲,调用位置的对象即将到期,但在构造函数内部直到完成它不再过期,因为它可以在构造函数块中稍后使用.

ArrayWrapper f();
ArrayWrapper r = f();   // [1]
Run Code Online (Sandbox Code Playgroud)

在[1]中,表达式f()引用一个临时的,它将在调用构造函数后到期,因此它可以被一个rvalue-reference绑定.

ArrayWrapper (ArrayWrapper&& other) 
    : _p_vals( other._p_vals  ) 
    , _metadata( other._metadata )        // [2] 
{ 
    other._p_vals = NULL; 
    std::cout << other._metadata << "\n"; // [3]
} 
Run Code Online (Sandbox Code Playgroud)

在构造函数内部,other没有到期,它将对构造函数的每个指令都是活动的.如果编译器允许在[2]中移动,则可能在[3]中进一步使用该变量将无效.您必须明确告诉编译器您希望该值现在到期.

  • @PermanentGuest:对,rvalue-reference(一旦绑定)是一个左值. (3认同)

R. *_*des 9

other是一个左值,因为它是一个变量.命名引用是左值,无论它们是什么类型的引用.