为什么当一个成员无法移动时,整个封闭类无法移动?

Joh*_*itb 11 c++ move-constructor

struct MyObject {
  MyObject(int value):value(value) { }
  MyObject(MyObject const&o):value(o.value) { }

  int value;
};
Run Code Online (Sandbox Code Playgroud)

假设复制构造函数除了有用之外还做了一些事情.然后

std::function<void()> f() {
  MyObject o;
  std::vector<int> v;
  return [=]() { /* use v and o */ &o; &v; }
}
Run Code Online (Sandbox Code Playgroud)

vo首先复制到初始lambda对象,这很好.但是每次需要移动lambda对象时,它们都会被复制.虽然v 可以移动,但事实并非如此.那是因为lambda没有隐式移动构造函数,因为o没有移动构造函数或普通复制构造函数.

有人可以解释一下这背后的理由吗?

Dav*_*eas 6

我似乎记得这是两个极端之间的妥协,那些根本不想隐式生成移动构造函数的人,以及那些希望在大多数情况下自动生成构造函数的人.

在那些不想隐含生成移动构造函数的人中,Dave Abrahams写了一篇名为Implicit Move Must Go的文章.理由是,在某些情况下,即使成员是可移动的,隐式生成移动构造函数也可以打破不变量.

今年早些时候(2011年),委员会决定将隐式生成移动构造函数放在一个似乎强调现有代码在安全问题上的性能提升的决策中(1),Dave再次发表了博客.它没有谈论决定的具体细节,利弊,但对结果也不太满意.

编辑(来自Jerry Coffin):这是隐式声明移动构造函数的条件列表:

If the definition of a class X does not explicitly declare a move constructor, 
one will be implicitly declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator,
— X does not have a user-declared destructor, and
— the move constructor would not be implicitly defined as deleted.
Run Code Online (Sandbox Code Playgroud)

基本思想是在类中包含任何这些都表明隐式生成的移动ctor可能会出现错误行为.虽然这是正确的,但是列表中的条件对于确定来说既不必要也不充分,因此不会生成许多本来有用的ctors,并且可以生成许多会导致问题的ctors.更糟糕的是,这些规则已经很长很复杂,很少有人记住这些规则,修复它们可能至少会加倍.
[杰瑞的贡献结束/咆哮]

(1)感谢Gene Bushuyev对于为什么做出决定的见解


Tod*_*ner 1

有点猜测,但我怀疑这可能与例外有关。也就是说,移动构造函数实际上应该是 noexcept,但是让移动构造函数调用复制构造函数可能会引发异常。

(试图从这里刷新我的记忆,我认为这涵盖了这个问题)

编辑添加

而我的猜测是错误的。据我所知,正确的答案来自here。复制构造函数的存在表明该类具有不变量,默认生成的移动构造函数可能不尊重这些不变量,因此不应生成。