当我正在阅读boost / shared_ptr.hpp时,我看到了这段代码:
// 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中吗?
答案 0 :(得分:90)
我赞成了ildjarn的回答,因为我发现它既准确又幽默。 : - )
我正在提供一个替代答案,因为我假设是因为OP可能想要知道的问题的标题为什么标准是这样说的。
<强>背景强>
C ++已经隐式生成了复制成员,因为如果它没有,它将在1985年出生,因为它如此与C不兼容。在这种情况下,我们不会今天有这个对话因为C ++不存在。
话虽如此,隐含生成的副本成员类似于“与恶魔打交道”。如果没有它们,C ++就不可能诞生。但它们是邪恶的,因为它们在大量实例中默默地生成错误的代码。 C ++委员会并不愚蠢,他们知道这一点。
<强> C ++ 11 强>
现在C ++已经诞生,并且已经发展成为一个成功的成年人,委员会只想说:我们不再做隐式生成的复制成员了。他们太危险了。如果您想要一个隐式生成的副本成员,您必须选择加入该决定(而不是选择退出它)。但是考虑到如果这样做会破坏现有C ++代码的数量,那就等于自杀了。 巨大的向后兼容性问题非常合理。
所以委员会达成了妥协的立场:如果你宣布移动成员(遗留的C ++代码不能做),那么我们将假设默认的副本成员可能做错了。如果您需要,可以选择加入(=default
)。或者自己写。否则会隐式删除它们。我们在具有仅移动类型的世界中的最新经验表明,此默认位置实际上非常普遍(例如unique_ptr
,ofstream
,future
等。选择费用实际上很小= default
。
展望未来
委员会甚至愿意说:如果您编写了析构函数,则隐式复制成员可能不正确,因此我们将删除它们。这是C ++ 98/03“三个规则”。然而,即便这样也会破坏很多代码。但是委员会在C ++ 11中说过,如果你提供一个用户声明的析构函数,那么隐式生成的复制成员已被弃用。这意味着可以在将来的标准中删除此功能。现在,在这种情况下,您的编译器可能会在任何一天开始发出“已弃用的警告”(标准不能指定警告)。
<强>结论强>
所以要预先警告:几十年来,C ++已经成长并成熟。这意味着你父亲的C ++可能需要迁移才能处理你孩子的C ++。这是一个缓慢,渐进的过程,所以你不要举手,只是移植到另一种语言。但是 更改,即使很慢。
答案 1 :(得分:23)
因为C ++标准如此说 - §12.8/ 7:
如果类定义没有显式声明复制构造函数,则会声明隐式。 如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数被定义为已删除;否则,它被定义为默认。如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用后一种情况。因此,对于类定义
struct X { X(const X&, int); };
隐式声明了复制构造函数。如果用户声明的构造函数稍后定义为
X::X(const X& x, int i =0) { /* ... */ }
然后由于模糊性,任何使用X的复制构造函数都是错误的;无需诊断。
(强调我的。)