为什么用户定义的移动构造函数会禁用隐式复制构造函数?
在阅读 boost/shared_ptr.hpp 时,我看到了这段代码:
While I'm reading boost/shared_ptr.hpp, i saw this code:
// generated copy constructor, destructor are fine...
#if defined( BOOST_HAS_RVALUE_REFS )
// ... except in C++0x, move disables the implicit copy
shared_ptr( shared_ptr const & r ): px( r.px ), pn( r.pn ) // never throws
{
}
#endif
这里的注释生成的复制构造函数、析构函数都可以,除非在 C++11 中,移动禁用隐式复制"是什么意思?在C++11中,我们是否总是自己编写复制构造函数来防止这种情况?
What does the comment "generated copy constructor, destructor are fine except in C++11, move disables the implicit copy" mean here? Shall we always write the copy ctor ourselves to prevent this situation in C++11?
推荐答案
我赞成 ildjarn 的回答,因为我觉得它既准确又幽默.:-)
I've upvoted ildjarn's answer because I found it both accurate and humorous. :-)
我提供一个替代答案,因为我根据问题的标题假设 OP 可能想知道为什么标准这么说.
I'm providing an alternate answer because I'm assuming because of the title of the question that the OP might want to know why the standard says so.
背景
C++ 隐式地生成了复制成员,因为如果没有,它会在 1985 年死掉,因为它与 C 如此 不兼容.在那种情况下,我们不会今天进行这个对话是因为 C++ 不存在.
C++ has implicitly generated copy members because if it didn't, it would've been still-born in 1985 because it was so incompatible with C. And in that case we wouldn't be having this conversation today because C++ wouldn't exist.
话虽如此,隐式生成的副本成员类似于与魔鬼的交易".没有它们,C++ 就不会诞生.但它们是邪恶的,因为它们在大量实例中默默地生成了错误的代码.C++ 委员会不傻,他们知道这一点.
That being said, implicitly generated copy members are akin to a "deal with the devil". C++ couldn't have been born without them. But they are evil in that they silently generate incorrect code in a significant number of instances. The C++ committee isn't stupid, they know this.
C++11
现在 C++ 已经诞生,并且已经发展成为一个成功的成年人,委员会只想说:我们不再做隐式生成的复制成员了.他们太危险了.如果您想要一个隐式生成的副本成员,您必须选择加入该决定(而不是选择退出).然而,考虑到如果这样做会破坏现有的 C++ 代码的数量,那将无异于自杀.有一个巨大的向后兼容性问题,这是非常合理的.
Now that C++ has been born, and has evolved into a successful grownup, the committee would just love to say: we're not doing implicitly generated copy members any more. They are too dangerous. If you want an implicitly generated copy member you have to opt-in to that decision (as opposed to opt-out of it). However considering the amount of existing C++ code that would break if this was done, that would be tantamount to suicide. There is a huge backwards compatibility concern that is quite justified.
因此委员会达成了一个妥协的立场:如果您声明移动成员(遗留 C++ 代码不能这样做),那么我们将假设默认复制成员可能会做错事.如果需要,请选择加入(使用 =default
).或者自己写.否则,它们将被隐式删除.我们迄今为止在只有移动类型的世界中的经验表明,这个默认位置实际上很常见(例如 unique_ptr
、ofstream
、future
等).= default
选择加入的费用实际上非常小.
So the committee reached a compromise position: If you declare move members (which legacy C++ code can't do), then we're going to assume that the default copy members are likely to do the wrong thing. Opt-in (with =default
) if you want them. Or write them yourself. Otherwise they are implicitly deleted. Our experience to-date in a world with move-only types indicates that this default position is actually quite commonly what is desired (e.g. unique_ptr
, ofstream
, future
, etc.). And the expense of opting-in is actually quite small with = default
.
展望
委员会甚至想说:如果您编写了析构函数,则隐式复制成员很可能不正确,因此我们将删除它们.这是 C++98/03 的三规则".然而,即使那样也会破坏很多代码.但是委员会在 C++11 中表示,如果您提供用户声明的析构函数,则不推荐使用隐式生成副本成员.这意味着可以在未来的标准中删除此功能.并且在这种情况下,现在任何一天您的编译器都可能开始发出不推荐使用的警告"(标准无法指定警告).
The committee would love to even say: If you've written a destructor, it is likely that the implicit copy members are incorrect, so we will delete them. This is the C++98/03 "rule of three". However even that would break lots of code. However the committee has said in C++11 that if you provide a user-declared destructor, the implicit generation of copy members is deprecated. That means that this feature could be removed in a future standard. And that any day now your compiler might start issuing "deprecated warnings" in this situation (the standard can not specify warnings).
结论
所以要预先警告:C++ 已经成长并成熟了几十年.这意味着你父亲的 C++ 可能需要迁移来处理你孩子的 C++.这是一个缓慢、渐进的过程,因此您不会举手而只是移植到另一种语言.但它正在改变,即使缓慢.
So be forewarned: C++ has grown up and matured over the decades. And that means that your father's C++ may need migrating to deal with your child's C++. It is a slow, gradual process so that you don't throw up your hands and just port to another language. But it is change, even if slow.
相关文章