为什么用户定义的move-constructor会禁用隐式复制构造函数?

ama*_*jxq 38 c++ boost copy-constructor rvalue-reference c++11

当我正在阅读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
Run Code Online (Sandbox Code Playgroud)

注释"生成复制构造函数,析构函数是否正常,除了在C++ 11中,移动禁用隐式副本"是什么意思?我们是否总是自己编写副本来防止C++ 11中出现这种情况?

How*_*ant 97

我赞成了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,因为我只能推测为什么.; - ] (2认同)
  • +1.很棒的解释.委员会作出了相当明智的决定. (2认同)

ild*_*arn 24

因为C++标准如此说 - §12.8/ 7:

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

struct X {
    X(const X&, int);
};
Run Code Online (Sandbox Code Playgroud)

隐式声明了复制构造函数.如果用户声明的构造函数稍后定义为

X::X(const X& x, int i =0) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

然后,由于模糊性,任何使用X的复制构造函数都是不正确的; 无需诊断.

(强调我的.)