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

Do move constructors need attribute that are moveable?

我正在努力研究移动构造函数,并希望通过这个问题获得更多见解。这是一个简单的 class.

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

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

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

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

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

Bs = other.Bs;

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

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

或更好

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

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

强烈推荐阅读 Howard Hinnant, the person who contributed probably the most to the concept of move semantics: http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014

不,不是。您的线路:

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

最好的解决方案是:</code></p> <p>你是对的,它是空的。如果您不放置任何构造函数,编译器会为您完成。</p> <p>如果您想添加其他构造函数但让编译器添加它自己的移动构造函数,您可以显式默认它:</p> <pre><code>// bonus: noexcept when it can. A(A&&) = default;


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

让类型 C 成为不可移动的 class 但可复制的。

这是一个例子:

// the `C&&` from the move will bind to the `const C&` of the operator=
auto anotherC = std::move(aC);

此处的移动构造函数将是 "effective",因为它将在 A object 从类型 A 的右值构造时调用。它会工作得很好,创建另一个 object 的有效副本。但是,它并没有达到预期的效果,因为它复制了内部 std::vector object。这是一个性能问题,而不是正确性问题。

这里实际上有两个性能问题。首先是构造函数默认构造了B subobject,然后赋值给它。那太浪费了。要解决这个问题:

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

第二个是移动构造函数可以从另一个构造函数中窃取object。为此,实施应该从 B subobject:

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

最后,为了回答标题中的问题,std::move 可以很好地处理不可移动的类型。它所做的只是将左值转换为右值; non-movable 类型(通常)可以从右值复制,所以除非你处理的是非常反常的类型,否则只需对 object 中的几乎所有内容使用 std::move。 (移动一个 int 值可能看起来很奇怪,但它是无害的)。