【问题标题】:Why std::move is needed?为什么需要 std::move ?
【发布时间】:2012-07-28 07:17:07
【问题描述】:

我在 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

我错过了什么?

【问题讨论】:

  • 最好避免前导下划线以防止意外使用保留名称。
  • 养成的好习惯。在这个例子中,没有一个以大写字母开头,所以你没问题,但你很容易忘记不要这样做。
  • rvalue refs and std::move的可能重复
  • @MarkB :这不是我的代码。我只是从引用的文章中复制了它。

标签: c++ c++11


【解决方案1】:

你的假设并不真实。构造函数的参数是xvalue,它允许绑定右值引用,但是一旦绑定了右值引用,在构造函数内部,它就不再是xvalue,而是lvalue。从概念上讲,调用位置的对象是 expiring,但在构造函数内部并且在它完成之前它不再是 expiring,因为它可以稍后在构造函数块中使用。

ArrayWrapper f();
ArrayWrapper r = f();   // [1]

在 [1] 中,表达式f() 指的是一个临时的,它会在调用构造函数后过期,所以它可以被一个右值引用绑定。

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

在构造函数内部,other 不会过期,它会在构造函数的每一条指令中存活。如果编译器允许在[2] 中移动,那么进一步使用[3] 中的变量将是无效的。您必须明确告诉编译器您希望该值现在过期。

【讨论】:

  • 好的,我想我明白了。所以,这意味着 [1] 中的 f() 是一个 xvalue,但是,函数参数中的 'other' 不是 xvalue,对吧?
  • @PermanentGuest:对,右值引用(一旦绑定)就是左值。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2015-05-10
    • 1970-01-01
    • 1970-01-01
    • 2017-06-11
    • 2014-02-16
    • 2015-06-25
    • 2014-12-19
    • 1970-01-01
    相关资源
    最近更新 更多