46

当我阅读 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 中,移动禁用隐式复制”在这里是什么意思?我们是否应该总是自己编写复制 ctor 以防止在 C++11 中出现这种情况?

4

2 回答 2

118

我赞成 ildjarn 的回答,因为我发现它既准确又幽默。:-)

我提供了一个替代答案,因为我假设问题的标题是 OP 可能想知道标准为什么这么说。

背景

C++ 已经隐式生成了复制成员,因为如果它没有,它就会在 1985 年死产,因为它与 C 是如此不兼容。在那种情况下,我们今天就不会进行这个对话,因为 C++ 将不存在.

话虽如此,隐式生成的副本成员类似于“与魔鬼交易”。没有它们,C++ 就不可能诞生。但它们是邪恶的,因为它们在大量实例中默默地生成错误代码。C++ 委员会并不愚蠢,他们知道这一点。

C++11

现在 C++ 已经诞生,并且已经发展成为一个成功的大人,委员会只想说:我们不再做隐式生成的复制成员了。他们太危险了。如果您想要一个隐式生成的副本成员,您必须选择加入该决定(而不是选择退出)。但是考虑到如果这样做会破坏现有 C++ 代码的数量,那无异于自杀。有一个巨大的向后兼容性问题是非常合理的。

所以委员会达成了一个妥协的立场:如果你声明了 move 成员(遗留的 C++ 代码不能这样做),那么我们将假设默认的 copy 成员可能会做错事。=default如果需要,请选择加入(使用)。或者自己写。否则,它们将被隐式删除。迄今为止,我们在一个只有移动类型的世界中的经验表明,这个默认位置实际上是非常常见的(例如unique_ptrofstreamfuture等)。选择加入的费用实际上非常小= default

期待

委员会甚至想说:如果你写了析构函数,那么隐式复制成员很可能是不正确的,所以我们将删除它们。这是 C++98/03 “三法则”。然而,即使这样也会破坏很多代码。然而,委员会在 C++11 中表示,如果您提供用户声明的析构函数,则不推荐使用隐式生成的复制成员。这意味着可以在未来的标准中删除此功能。并且现在任何一天,您的编译器都可能在这种情况下开始发出“不推荐使用的警告”(标准不能指定警告)。

结论

所以请注意:C++ 已经成长和成熟了几十年。这意味着您父亲的 C++ 可能需要迁移以处理您孩子的 C++。这是一个缓慢而渐进的过程,因此您不会举手而只是移植到另一种语言。但它变化,即使缓慢。

于 2012-06-29T02:15:27.187 回答
31

因为 C++ 标准是这样说的——第 12.8/7 节:

如果类定义没有显式声明复制构造函数,则式声明一个。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数定义为已删除;否则,它被定义为默认值。如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用后一种情况。因此,对于类定义

struct X {
    X(const X&, int);
};

复制构造函数是隐式声明的。如果用户声明的构造函数后来被定义为

X::X(const X& x, int i =0) { /* ... */ }

那么由于歧义,对 X 的复制构造函数的任何使用都是格式错误的;不需要诊断。

(强调我的。)

于 2012-06-29T01:45:09.373 回答