理解移动语义

时间:2014-03-31 13:56:11

标签: c++ c++11 move

我试图让我的头脑移动语义。特别是我想知道如何创建一个仅仅移动'类型。这是我的尝试:

class Movable {
    Movable(const Movable&) = delete;
    Movable &operator=(Movable &) = delete;

public:
    Movable() { }
    Movable(Movable&& rhs) { cout << "mov constructor called" << endl; }
    Movable &operator=(Movable &&rhs) { cout << "Mov assignment called" << endl; return *this; }
};

int main() {

    // (1) This works correctly
    Movable mov_constructored = ([]{
        return Movable();
    })();

    // (2) Why do I have to explicity use std::move?
    Movable mov_assigned = std::move(mov_constructored);

    // (3) The last line fails because it's trying to use the copy constructor.
    // Is it possible to pass by mov?
    mov_assigned = std::move(([](Movable mov_passed){
        return mov_passed;
    })(mov_constructored));
}

我的主要问题是(1)为什么#2要求我明确表达我想要移动而不是复制?看起来像默认行为,如果没有复制构造函数,请使用移动构造函数(假设它存在)。除非您明确声明移动语义,否则实际行为似乎只会失败。

问题(2)基本上是什么是传递仅移动对象的正确方法?是通过引用传递的唯一正确方法(因为移动语义可能只涉及赋值/返回值?)或者是否有实际的方法来移动&#39;一个对象变成一个函数?

2 个答案:

答案 0 :(得分:5)

1)因为move_constructed是左值,所以它会尝试调用左值分配。我们使用std::move()将左值引用转换为右值引用(注意std::move()不移动,它只是一个转换)。

2)同样,问题是move_constructed是左值,因此使用副本ctor初始化by-value参数。如果传递右值(或使用std::move()将左值转换为右值),则该调用应该有效,因为使用move ctor初始化了by-value参数。 (见注释)

注意:从C ++ 11开始,我更喜欢谈论 左值构造函数 rvalue构造函数 ,而不是经典复制构造函数和C ++ 11 移动构造函数。我认为这个措辞很容易理解lvalue vs rvalue / move语义:

经典 copy ctor 只是一个构造函数,使用左值初始化对象。由于它是一个左值,我们不能偷走它的资源,因为可能的左值将在以后使用。所以我们必须复制它。

移动ctor只是一个构造函数,它使用rvalue来初始化一个对象。由于它是一个rvalue,我们可以安全地窃取它的数据/资源,因为rvalue将在以后超出范围。

答案 1 :(得分:2)

在C ++ 11中,它以相反的方式工作 - 如果它无法移动,它会回退到复制。复制被认为是更安全的操作,而移动被认为是更快的操作。因此,在适用的情况下,将尝试移动以获得性能,但将返回到副本。如果您有以下声明:

Movable mov_assigned = mov_constructored;

然后,这是明确请求副本,并且移动不被视为副本的安全替代。在该语句之后,C ++程序员不会期望mov_constructored发生变化。