移动构造函数是否需要可移动的属性?

时间:2016-11-02 18:25:43

标签: c++ object constructor move move-semantics

我正试图围绕移动构造函数,并希望通过这个问题获得更多的见解。这是一个简单的课程。

class A
{
 private:
   vector<B> Bs;
 public:
   /*
   ..
   */

   A(A&& other)
   : Bs
   {
      Bs = other.Bs;
   }
}

即使B没有移动构造函数,我的移动构造函数看起来是否正确?即使我没有明确为类B的对象编写移动赋值,移动构造函数是否仍然有效?如果不是,这是否意味着如果想要移动任何物体,他首先必须确保每个属性也是可移动的?

3 个答案:

答案 0 :(得分:4)

如果您的对象包含可移动的对象(例如std::vector),则默认的移动构造函数将负责移动,因此您无需执行任何操作。尝试使用Rule of Zero

在你的情况下,不,移动ctor不会做正确的事情。它将复制,因为在

Bs = other.Bs;

other.Bs是函数内部的左值,因为它有一个名称(是的,它指的是右值引用,但other本身是左值)。你需要

Bs = std::move(other.Bs);

或更好

A(A&& other) : Bs(std::move(other.Bs)) {}

但是,在这种情况下,你真的不应该编写任何用户定义的移动构造函数。

强烈建议您阅读Howard Hinnant,这个人对移动语义的概念贡献最大:http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014

答案 1 :(得分:3)

不,不是。你的话在这里:

var newEntry = {};
newEntry[$scope.xxxx.key] = {
    name: $scope.xxx.name,
    age: $scope.xxx.age,
    email: $scope.xxx.email,
};
xxx.$add( newEntry );

您正在进行复制作业。在移动构造函数体中不会更改表达式值类型。左值仍然是左值,右值仍然是左值。

要进行移动分配,它看起来像这样:

Bs = other.Bs;

但它仍然没有那么高效。

还有另一个问题。您的代码未编译,因为您已为Bs = std::move(other.Bs); 构造函数提交了大括号。事实上,这是你应该移动价值的地方。这是一个例子:

std::vector

最好的解决方案是:// a kitten dies when your move constructor is not noexcept A(A&& other) noexcept // in the move constructor of A, we move construct it's member too. : Bs{std::move(other.Bs)} // empty body {}

你是对的,它是空的。如果你没有放任何构造函数,编译器会为你做。

如果你想添加其他构造函数但让编译器添加它自己的移动构造函数,你可以显式默认它:

最后一件事。如果你的类包含另一个不可移动但可复制的类,它将尽其所能:复制构造它。

让类型// bonus: noexcept when it can. A(A&&) = default; 成为不可移动的类但可以复制。

以下是一个例子:

C

答案 2 :(得分:1)

这里的移动构造函数将是&#34;有效的&#34;因为A对象是由A类型的右值构造的,所以它将被调用。它可以正常工作,创建另一个对象的有效副本。但是,它没有那么有效,因为它复制了内部std::vector对象。这是一个性能问题,而不是正确性问题。

这里实际上有两个性能问题。第一个是构造函数默认构造B子对象,然后分配给它。这很浪费。解决这个问题:

A::A(A&& other) : B(other.B) {
}

第二个是允许移动构造函数从另一个对象窃取。为此,实现应从B子对象移动:

A::A(A&& other) : B(std::move(other.B)) {
}

最后,为了回答标题中的问题,std::move适用于不可移动的类型。它所做的只是将左值转换为右值;不可移动的类型可以(通常)从rvalues复制,因此除非你处理一个非常不正常的类型,只需在对象中的几乎所有东西上使用std::move。 (移动int值可能看起来很奇怪,但它无害)。