简体   繁体   English

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

[英]Do move constructors need attribute that are moveable?

I am trying to wrap my head around move constructors and am hoping to get some more insights through this question. 我试图围绕移动构造函数,并希望通过这个问题获得更多的见解。 Here is a simple class. 这是一个简单的课程。

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

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

Does my move constructor looks correct even if B does not have a move constructor? 即使B没有移动构造函数,我的移动构造函数看起来是否正确? Will the move constructor be effective even if I haven't explicitly written a move assignment for the object of class B ? 即使我没有明确为B类对象编写移动赋值,移动构造函数是否有效? If not, does it mean that if one wants to move any object, (s)he first have to ensure each attribute is also moveable? 如果不是,这是否意味着如果想要移动任何物体,他首先必须确保每个属性也是可移动的?

If your object contains objects that are move-able (such as std::vector ), the default move constructor will take care of the move, so you don't need to do anything. 如果您的对象包含可移动的对象(例如std::vector ),则默认移动构造函数将负责移动,因此您无需执行任何操作。 Try to use the Rule of Zero . 尝试使用零规则

In your case, no, the move ctor won't do the right thing. 在你的情况下,不,移动ctor将不会做正确的事情。 It will copy, since in 它将复制,因为在

Bs = other.Bs;

other.Bs is a lvalue inside the function since it has a name (yes, it refers to a rvalue reference but other itself is a lvalue). other.Bs是函数内部的左值,因为它有一个名称(是的,它指的是右值引用,但other本身是左值)。 You need 你需要

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

or better 或更好

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

But again, in this case you really shouldn't write any user-defined move constructor at all. 但同样,在这种情况下,您根本不应该编写任何用户定义的移动构造函数。

Highly recommended read from Howard Hinnant , the person who contributed probably the most to the concept of move semantics: http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014 强烈推荐来自Howard Hinnant的读物,他对移动语义的概念贡献最大: http//www.slideshare.net/ripplelabs/howard-hinnant-accu2014

No it is not. 不它不是。 Your line here: 你的路线在这里:

Bs = other.Bs;

You are doing copy assignment. 你正在做副本任务。 Being in the move constructor body won't change expression value type. 在移动构造函数体中不会更改表达式值类型。 lvalue are still lvalues and rvalue are still rvalue. 左值仍然是左值,右值仍然是左值。

To do move assignment, it would look like this: 要进行移动分配,它看起来像这样:

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

But it is still not as efficient as it could be. 但它仍然没有尽可能高效。

There's another problem. 还有另一个问题。 Your code is not compiling because you have committed the braces for the std::vector constructor. 您的代码未编译,因为您已为std::vector构造函数提交了大括号。 In fact, this is where you should move the value. 事实上,这是你应该移动价值的地方。 Here's an example: 这是一个例子:

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

The best solution of all is this: 最好的解决方案是:

You are right, it's empty. 你是对的,它是空的。 If you don't put any constructors, the compiler will do it for you. 如果您没有放置任何构造函数,编译器将为您执行此操作。

If you want to add other constructor but let the compiler add it's own move constructor, you can explicitly default it: 如果你想添加其他构造函数但让编译器添加它自己的移动构造函数,你可以显式默认它:

// bonus: noexcept when it can.
A(A&&) = default;

One last thing. 最后一件事。 If your class contains another one that is not moveable but copiable, it will do what it can: copy construct it. 如果您的类包含另一个不可移动但可复制的类,它将尽其所能:复制构造它。

Let the type C be a non-moveable class but copiable. C型成为不可移动的类但可以复制。

Here's an example: 这是一个例子:

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

The move constructor here will be "effective" in that it will be called when an A object is constructed from an rvalue of type A . 这里的移动构造函数将是“有效的”,因为当A对象是由类型A的右值构造时,它将被调用。 It will work just fine, creating a valid copy of the other object. 它可以正常工作,创建另一个对象的有效副本。 However, it's not as effective as it could be, since it copies the internal std::vector object. 但是,它没有那么有效,因为它复制了内部的std::vector对象。 That's a performance issue, not a correctness issue. 这是性能问题,而不是正确性问题。

There are actually two performance issues here. 这里实际上有两个性能问题。 The first is that the constructor default constructs the B subobject, then assigns to it. 第一个是构造函数默认构造B子对象,然后分配给它。 That's wasteful. 这太浪费了。 To fix that: 解决这个问题:

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

The second is that a move constructor is allowed to steal from the other object. 第二个是允许移动构造函数从另一个对象窃取。 To do that, the implementation should move from the B subobject: 为此,实现应该从B子对象移动:

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

Finally, to answer the question in the title, std::move works just fine with types that aren't movable. 最后,为了回答标题中的问题, std::move适用于不可移动的类型。 All it does is convert an lvalue to an rvalue; 它所做的只是将左值转换为右值; non-movable types can (usually) be copied from rvalues, so unless you're dealing with a very perverse type, just use std::move on pretty much everything in the object. 不可移动的类型可以(通常)从rvalues复制,所以除非你正在处理一个非常不正常的类型,只需在对象中的几乎所有东西上使用std::move (It might look strange to move an int value, but it's harmless). (移动int值可能看起来很奇怪,但它是无害的)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM