为什么用户定义的移动构造函数禁用隐式复制构造函数
Why user-defined move-constructor disables the implicit copy-constructor?
当我阅读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中出现这种情况?
我赞成ildjarn的回答,因为我发现它既准确又幽默。
我提供了一个替代答案,因为我假设因为问题的标题,OP 可能想知道为什么标准这么说。
背景
C++隐式生成了副本成员,因为如果没有,它将在 1985 年胎死腹中,因为它与 C 非常不兼容。 在那种情况下,我们今天就不会进行这次对话,因为C++不存在。
话虽如此,隐式生成的副本成员类似于"与魔鬼的交易"。 没有他们,C++不可能出生。 但它们是邪恶的,因为它们在大量实例中默默地生成了不正确的代码。 C++委员会并不愚蠢,他们知道这一点。
C++11
现在C++已经诞生,并已经发展成为一个成功的成年人,委员会很想说:我们不再做隐式生成的复制成员了。 他们太危险了。 如果你想要一个隐式生成的复制成员,你必须选择加入该决定(而不是选择退出它)。 然而,考虑到如果这样做会破坏的现有C++代码的数量,这无异于自杀。 存在一个巨大的向后兼容性问题,这是非常合理的。
因此,委员会达成了一个妥协的立场:如果你声明移动成员(遗留C++代码无法做到),那么我们将假设默认的复制成员可能会做错的事情。 选择加入(带=default
)如果你需要的话。 或者自己写。 否则,它们将被隐式删除。 我们迄今为止在仅移动类型的世界中的经验表明,这种默认位置实际上是非常普遍的(例如 unique_ptr
、ofstream
、future
等)。 而且选择加入的费用实际上很小 = default
.
期待
委员会甚至想说:如果你写了一个析构函数,那么隐式副本成员很可能是不正确的,所以我们会删除它们。 这就是C++98/03的"三法则"。 然而,即使这样也会破坏很多代码。 但是,委员会在 C++11 中表示,如果您提供用户声明的析构函数,则不推荐使用隐式生成复制成员。1, 2 这意味着此功能可能会在未来的标准中删除。 并且现在任何一天您的编译器都可能在这种情况下开始发出"弃用警告"(标准不能指定警告)。
结论
所以要预先警告:几十年来,C++已经成长和成熟。 这意味着你父亲的C++可能需要迁移来处理你孩子的C++。 这是一个缓慢、渐进的过程,这样你就不会举手投降,只是移植到另一种语言。 但这是变化,即使缓慢。
<小时 />1 http://eel.is/c++draft/class.mem#class.copy.ctor-6.sentence-3
2 http://eel.is/c++draft/class.mem#class.copy.assign-2.sentence-3
因为C++标准是这样说的 – §12.8/7:
如果类定义未显式声明复制构造函数,则隐式声明一个。如果类定义声明移动构造函数或移动赋值运算符,则隐式声明的复制构造函数定义为已删除;否则,它被定义为默认值。如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用后一种情况。因此,对于类定义
struct X { X(const X&, int); };
复制构造函数是隐式声明的。如果用户声明的构造函数稍后定义为
X::X(const X& x, int i =0) { /* ... */ }
那么,由于歧义,对 X 的复制构造函数的任何使用都是格式不正确的;不需要诊断。
(强调我的。
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 当从函数参数中的临时值调用复制构造函数时
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 使用复制构造函数复制双精度数组
- C 无可行的构造函数复制类型的变量
- 没有可行的构造函数复制类型 'MyString' 的数组元素
- 编译时,复制构造函数/复制分配和正常功能调用优化之间是否存在任何区别
- 如何最小化调用列表构造函数(复制构造函数)的次数?
- C 11矢量构造函数复制与范围
- 我定义了一个非复制构造函数;复制构造函数还会被隐式定义吗
- 可以将构造函数复制为转换运算符
- 将基类指针的构造函数复制到子类
- C++树类:构造函数/复制/内存泄漏
- 如何制作这个在模板构造函数复制中使用类型定义的类型的模板
- 将构造函数复制为模板化的成员函数
- 绕过私有复制构造函数/复制赋值C++
- C++通过构造函数复制对象
- 复制构造函数 - 复制C++中的对象
- 将带unique_ptr的类的构造函数复制到作为成员的抽象类