C++ 为什么当一个成员不能移动时,整个封闭类就不能移动?

C++ 为什么当一个成员不能移动时,整个封闭类就不能移动?,c++,move-constructor,C++,Move Constructor,范例 假设复制构造函数除了有用之外还做了一些事情。然后 struct MyObject { MyObject(int value):value(value) { } MyObject(MyObject const&o):value(o.value) { } int value; }; std::函数f(){ 对象o; std::向量v; 返回[=](){/*使用v和o*/&o;&v;} } v和o首先复制到初始lambda对象中,这很好。但是,每次需要移动lambda对

范例

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

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

  int value;
};
std::函数f(){
对象o;
std::向量v;
返回[=](){/*使用v和o*/&o;&v;}
}
v
o
首先复制到初始lambda对象中,这很好。但是,每次需要移动lambda对象时,都会再次复制它们。即使
v
可以移动,但它不能移动。这是因为lambda没有隐式移动构造函数,因为
o
没有移动构造函数或普通复制构造函数


有人能解释一下这背后的原因吗?

有点猜测,但我怀疑这可能与例外情况有关。也就是说,移动构造函数实际上应该是noexcept,但是让一个移动构造函数调用一个复制构造函数可能会抛出它

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

编辑以添加


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

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

在那些不希望隐式生成移动构造函数的人中,Dave Abrahams写了一篇文章,名为。其基本原理是,在某些情况下,即使成员是可移动的,移动构造函数的隐式生成也会破坏不变量

今年年初(2011年),委员会决定在一项决定中保留隐式生成的move构造函数,该决定似乎强调现有代码中的性能提升,而不是安全问题(1),Dave对此再次表示关注。它没有谈论决定的细节、利弊,但对结果也不太满意

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

std::function<void()> f() {
  MyObject o;
  std::vector<int> v;
  return [=]() { /* use v and o */ &o; &v; }
}
其基本思想是,在类中包含这些元素中的任何一个都表明隐式生成的move-ctor可能会出现错误行为。虽然这是真的,但列表中的条件对确定既不必要也不充分,因此没有生成许多有用的move-ctor,并且可以生成许多会导致问题的move-ctor。更糟糕的是,这些规则已经足够长和复杂,几乎没有人能完全记住它们,修复它们可能至少会使这一点翻一番。
[杰瑞的贡献/咆哮结束]


(1) 感谢Gene Bushuyev对做出决定的原因的洞察

听起来这是一个默认移动构造函数(和赋值操作符?)的问题,你可能想在问题中包括这些术语。@LokiAstari我想你的意思是2125如果一个类有一个不可移动的成员,这难道不是一个类没有隐式移动构造函数的原因吗?我通过在mover类中包装变量来处理这个问题,mover类在非const copy-ctor中移动:我认为原理很简单,一旦有一个不可移动的成员,所有其他成员都不能移动,或者当他们投掷时,不变量将被破坏。我不会说这个决定是政治性的,我认为,考虑到隐式移动,许多现有代码不需要任何更改就可以获得性能提升,这一点压倒了安全性。@dribeas:希望您不介意编辑--关于最初的问题,我没有太多要补充的内容,但是,可以/将要产生一项动议的条件的数量和复杂性似乎与反对意见有关。可能还值得一提的是,最后一个条件本身实际上是比上述条件更长的另一个条件列表。@JerryCoffin,我绝对不会为此感谢你,但我不认为使用(at)语法的评论会通知你:)啊,谢谢,先生,这是有意义的。lambda没有不变量。你认为它应该总是有一个移动构造函数吗?如果它有例外,它们就不需要一个简单的复制构造函数,而是一个noexcept复制构造函数。嗯,等等。成员的副本构造函数的存在表明该成员具有不变量,但这些不变量仍然会得到尊重,因为该成员将永远不会被移动。我不明白为什么异常会很重要。如果有例外,就有例外,那又怎样。这是正常的行为。移动异常主要与
vector
相关,vector希望执行不能失败的无声移动。
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.