为什么当一个成员不能移动时,整个封闭类也不能移动?
示例
struct MyObject {
MyObject(int value):value(value) { }
MyObject(MyObject const&o):value(o.value) { }
int value;
};
假设复制构造函数除了有用之外还执行其他操作。然后,
std::function<void()> f() {
MyObject o;
std::vector<int> v;
return [=]() { /* use v and o */ &o; &v; }
}
首先将 v
和 o
复制到初始 lambda 对象中,这样就可以了。但每次需要移动 lambda 对象时,它们都会被再次复制。尽管v
可以被移动,但事实并非如此。这是因为 lambda 没有隐式移动构造函数,因为 o
没有移动构造函数或简单的复制构造函数。
有人可以解释一下这背后的理由吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我似乎记得这是两个极端之间的折衷方案,即那些根本不希望隐式生成移动构造函数的人,以及那些希望在大多数情况下自动生成移动构造函数的人。
在那些不希望隐式生成移动构造函数的人中,Dave Abrahams 写了一篇名为 隐式移动必须进行。其基本原理是,在某些情况下,即使成员是可移动的,移动构造函数的隐式生成也可能会破坏不变量。
今年年初(2011 年),委员会决定保留移动构造函数的隐式生成,该决定似乎强调现有代码的性能提升而不是安全问题(1),Dave 再次博客有关它。它没有谈论该决定的具体细节、利弊,但对结果也不太满意。
编辑(来自 Jerry Coffin):这是隐式声明移动构造函数的条件列表:
基本思想是,在类中包含任何这些条件都表明隐式生成的移动构造函数可能会出现错误行为。虽然确实如此,但列表中的条件对于确定来说既不是必要的也不是充分的,因此没有生成许多有用的移动向量,并且可以生成许多会导致问题的移动向量。更糟糕的是,这些规则已经足够长和复杂,很少有人能记住它们,而修复它们可能至少会增加一倍。
[Jerry 的贡献/咆哮结束]
(1) 感谢 Gene Bushuyev 提供了关于为何做出该决定的见解
I seem to recall that this is a compromise between the two extremes, those who did not want implicit generation of move constructors at all, and those that wanted move constructors to be generated automatically in most circumstances.
Among the people that wanted no implicit generation of move constructors, Dave Abrahams wrote an article called Implicit Move Must Go. The rationale there is that under some circumstances, even if the members are movable, the implicit generation of a move constructor can break invariants.
Early this year (2011) the committee decided to keep implicit generation of move constructors in a decision that seemed to emphasize performance boost in existing code over safety issues (1), and again Dave blogged about it. It does not talk about the specifics of the decision, the pros and cons, but is not quite happy with the result either.
Edit (from Jerry Coffin): Here's the list of conditions for implicit declaration of a move constructor:
The basic idea is that inclusion of any of these in the class is an indication that an implicitly generated move ctor is likely to mis-behave. While that's true, the conditions in the list are neither necessary nor sufficient to the determination, so many move ctors that would have been useful aren't generated, and many that will cause problems can be generated. Worse, the rules are already long and complex enough that few remember them all, and fixing them would probably at least double that.
[end of Jerry's contribution/rant]
(1) Thanks to Gene Bushuyev for the insight as to why the decision was taken
有点猜测,但我怀疑这可能与例外有关。也就是说,移动构造函数实际上应该是 noexcept,但是让移动构造函数调用复制构造函数可能会引发异常。
(试图从这里刷新我的记忆,我认为涵盖了这个问题)
编辑添加:
我的猜测是错误的。据我所知,正确的答案来自此处。复制构造函数的存在表明该类具有不变量,默认生成的移动构造函数可能不尊重这些不变量,因此不应生成。
Kinda guessing, but I suspect it may have to do with exceptions. That is, move constructors should really be noexcept, but having a move constructor call a copy constructor could have it throw.
(trying to refresh my memory from here, which I think covered this issue)
EDITED TO ADD:
And my guess was wrong. The correct answer, as far as I can tell, is from here. The presence of the copy constructor is an indication that the class has invariants, and the default generated move constructor might not respect those invariants, and therefor, should not be generated.