Move ctor 是否在所有情况下都有效?

Will the Move ctor effective in all scenarios?

我刚刚从 https://msdn.microsoft.com/en-us/library/dd293665.aspx 中看到下面的代码,它展示了如何实现移动 ctor:

MemoryBlock(MemoryBlock&& other)
   : _data(nullptr)
   , _length(0)
{
   std::cout << "In MemoryBlock(MemoryBlock&&). length = " 
             << other._length << ". Moving resource." << std::endl;

   // Copy the data pointer and its length from the 
   // source object.
   _data = other._data;
   _length = other._length;

   // Release the data pointer from the source object so that
   // the destructor does not free the memory multiple times.
   other._data = nullptr;
   other._length = 0;
}

此处随此代码一起出现的 question/doubt 之一是:如果 class MemoryBlock 包含一些 class 类型的成员变量(比如 someclassvar ), 如果该成员相当大, 修改后的 move ctor 中的以下行是否有效 (假设此 someclassvar 没有 move ctor)?

MemoryBlock(MemoryBlock&& other)
   : _data(nullptr)
   , _length(0),someclassvar(other.someclassvar)
{
   std::cout << "In MemoryBlock(MemoryBlock&&). length = " 
             << other._length << ". Moving resource." << std::endl;

   // Copy the data pointer and its length from the 
   // source object.
   _data = other._data;
   _length = other._length;
  // someclassvar=other.someclassvar;

   // Release the data pointer from the source object so that
   // the destructor does not free the memory multiple times.
   other._data = nullptr;
   other._length = 0;
   delete other.someclassvar;
}

这里someclassvar = other.someclassvar;我相信这会调用复制赋值运算符,那么这种行为有效吗?而且,如果 someclassvar 同时包含移动赋值和复制赋值,这应该调用?

像这样的所有情况下,move ctor 是否仍然有效?如果没有,复制ctor更好吗?

MemoryBlock(MemoryBlock&& other)
   : _data(nullptr)
   , _length(0), someclassvar(std::move(other.someclassvar))
{
   std::cout << "In MemoryBlock(MemoryBlock&&). length = " 
             << other._length << ". Moving resource." << std::endl;

   // Copy the data pointer and its length from the 
   // source object.
   _data = other._data;
   _length = other._length;

   // Release the data pointer from the source object so that
   // the destructor does not free the memory multiple times.
   other._data = nullptr;
   other._length = 0;
}

如果 someclassvar class 有 move ctor/ move = 那么 std::move(other.someclassvar)move 否则会 copy.

如果需要高效,请为 class someclassvar 提供 move ctor/ move =

首先,在任何构造函数中,如果可能,您应该更愿意在构造函数的初始化列表中初始化您的成员。其次,在移动构造函数中,您应该使用 std::move 来移动可移动的成员或复制它们(如果它们不可移动):

MemoryBlock(MemoryBlock&& other)
: _data(std::move(other._data)), 
  _length(std::move(other._length)),
  someclassvar(std::move(other._someclassvar)) {
   // optionally set state of moved object
}

现在 std::move 不会移动任何对象,而是将它们转换为右值引用。因此,由于重载决议,将发生两件事:

  1. 如果要移动的类型具有用户定义的或隐式定义的移动构造函数,将调用此移动构造函数,因此对象将相应地移动到其新宿主。
  2. 如果要移动的类型没有任何定义的移动构造函数,那么将调用该类型的复制构造函数。这是因为右值引用可以绑定到 const 左值引用,因此幸运的是移动构造函数复制构造函数可以匹配重载解析。

Live Demo

现在复制赋值和移动赋值大致相同。正在做:

someclassvar = other.someclassvar;

会将 other.someclassvar 复制分配给 someclassvar。为什么?由于过载决议规则。但是,如果您这样做:

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

如果 someclassvar 有一个移动赋值运算符,那么出于与上述相同的原因,它将被调用。另一方面,如果 someclassvar 没有移动赋值运算符,它的赋值运算符将被调用。这里不涉及构造函数,因为该语句是一个赋值。