为什么需要std :: move?

时间:2012-07-30 15:30:17

标签: c++ c++11

我在C ++ 11中的移动语义上读了一个beautiful article。本文以非常直观的方式编写。本文中的示例类如下所示。

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

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

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

到目前为止,非常好。

标准说:

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

  

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

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

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

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

  •   
  • .*指向成员的表达式,其中第一个操作数是   xvalue和第二个操作数是指向数据成员的指针。

  •   

我的问题:

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

我错过了什么?

2 个答案:

答案 0 :(得分:15)

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

lvalue

在[1]中,表达式ArrayWrapper f(); ArrayWrapper r = f(); // [1] 指的是一个临时的,在调用构造函数后将到期,因此它可以被rvalue-reference绑定。

f()

在构造函数中,ArrayWrapper (ArrayWrapper&& other) : _p_vals( other._p_vals ) , _metadata( other._metadata ) // [2] { other._p_vals = NULL; std::cout << other._metadata << "\n"; // [3] } 没有到期,它对于构造函数的每个指令都是活动的。如果编译器允许在[2]中移动,则可能无法在[3]中进一步使用该变量。您必须明确告诉编译器您希望该值到期 now

答案 1 :(得分:9)

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