我试图让我的头脑移动语义。特别是我想知道如何创建一个仅仅移动'类型。这是我的尝试:
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;一个对象变成一个函数?
答案 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
发生变化。