自动生成默认/复制/移动 ctor 和复制/移动赋值运算符的条件?
Posted
技术标签:
【中文标题】自动生成默认/复制/移动 ctor 和复制/移动赋值运算符的条件?【英文标题】:Conditions for automatic generation of default/copy/move ctor and copy/move assignment operator? 【发布时间】:2011-06-24 01:23:14 【问题描述】:我想在编译器通常自动生成默认构造函数、复制构造函数和赋值运算符的条件下刷新我的记忆。
我记得有一些规则,但我不记得了,也找不到网上有信誉的资源。有人可以帮忙吗?
【问题讨论】:
【参考方案1】:在下文中,“自动生成”的意思是“隐式声明为默认值,但未定义为已删除”。在某些情况下,特殊成员函数已声明,但定义为已删除。
如果没有用户声明的构造函数(第 12.1/5 节),则会自动生成默认构造函数。 如果没有用户声明的移动构造函数或移动赋值运算符,则会自动生成复制构造函数(因为在 C++03 中没有移动构造函数或移动赋值运算符,这在 C++ 中简化为“始终” 03) (§12.8/8)。 如果没有用户声明的移动构造函数或移动赋值运算符(第 12.8/19 节),则会自动生成复制赋值运算符。 如果没有用户声明的析构函数(第 12.4/4 节),则会自动生成析构函数。仅限 C++11 及更高版本:
如果没有用户声明的复制构造函数、复制赋值运算符或析构函数,并且生成的移动构造函数有效(第 12.8/10 节),则会自动生成移动构造函数。 如果没有用户声明的复制构造函数、复制赋值运算符或析构函数,并且生成的移动赋值运算符有效(例如,如果它不需要分配常量成员),则会自动生成移动赋值运算符( §12.8/21)。【讨论】:
继承的析构函数算不算?我的意思是,假设我有一个带有空虚拟析构函数的基类。它会阻止在子类中创建移动构造函数吗?如果答案是肯定的,如果我在基类中定义一个移动构造函数会有帮助吗? 我认为你应该提一下,在类中有const
成员会阻止自动生成构造函数...
Does “在某些情况下,特殊成员函数被声明,但被定义为已删除。”例如,参考您在哪里有 const 或无法移动的参考成员?不,那不可能,因为将应用副本。
我知道这个论坛限制发送超链接。但这也是一篇好文章——cplusplus.com/articles/y8hv0pDG
请注意,作为标准,隐式默认的复制构造函数“如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用”(12.8 复制和移动类对象 [class.copy]).【参考方案2】:
我发现下面的图表非常有用。
来自Sticky Bits - Becoming a Rule of Zero Hero
【讨论】:
美丽。 “独立”指的是什么?独立于什么? 复制 ctor/assignment 彼此“独立”。如果你只写一个,编译器会提供另一个。相反,如果您提供移动 ctor 或移动赋值,编译器将不会提供另一个。 想知道复制操作独立的原因是什么。历史原因可能是什么?或者副本不会修改它的目标但移动的事实? @Explorer_N 是的,向后兼容,所以历史原因。很久以前这是一个糟糕的设计选择,所以现在需要像“三规则”(定义所有 3 或无:复制构造函数、复制赋值运算符和通常的析构函数)这样的良好实践来避免难以发现的错误。 @MarcoM.,据我了解,“If you write...”条件包括将特殊成员函数设置为= delete
(很明显)或@987654325这两种情况@(对我来说不太明显)。我说的对吗?【参考方案3】:
C++17 N4659 标准草案
如需快速跨标准参考,请查看以下 cppreference 条目的“隐式声明”部分:
https://en.cppreference.com/w/cpp/language/copy_constructor https://en.cppreference.com/w/cpp/language/move_constructor https://en.cppreference.com/w/cpp/language/copy_assignment https://en.cppreference.com/w/cpp/language/move_assignment当然可以从标准中获得相同的信息。例如。在C++17 N4659 standard draft:
15.8.1 “复制/移动构造函数”说用于复制构造函数:
6 如果类定义没有显式声明复制构造函数,则隐式声明非显式构造函数。 如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的副本 构造函数被定义为删除;否则,它被定义为默认值 (11.4)。如果后一种情况不推荐使用 该类具有用户声明的复制赋值运算符或用户声明的析构函数。
对于移动构造函数:
8 如果类 X 的定义没有显式声明移动构造函数,则将隐式声明一个非显式构造函数 声明为默认当且仅当
(8.1) — X 没有用户声明的复制构造函数,
(8.2) — X 没有用户声明的复制赋值运算符,
(8.3) — X 没有用户声明的移动赋值运算符,并且
(8.4) — X 没有用户声明的析构函数。
15.8.2 “复制/移动赋值运算符”表示复制赋值:
2 如果类定义没有显式声明复制赋值运算符,则隐式声明一个。 如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的 复制赋值运算符定义为删除;否则,它被定义为默认值 (11.4)。后者 如果类具有用户声明的复制构造函数或用户声明的析构函数,则不推荐使用 case。
对于移动分配:
4 如果类 X 的定义没有显式声明移动赋值运算符,则将隐式 声明为默认当且仅当
(4.1) — X 没有用户声明的复制构造函数, (4.2) — X 没有用户声明的移动构造函数, (4.3) — X 没有用户声明的复制赋值运算符,并且 (4.4) — X 没有用户声明的析构函数。
15.4 “析构函数”对析构函数这么说:
4 如果类没有用户声明的析构函数,则析构函数被隐式声明为默认值 (11.4)。一个 隐式声明的析构函数是其类的内联公共成员。
【讨论】:
以上是关于自动生成默认/复制/移动 ctor 和复制/移动赋值运算符的条件?的主要内容,如果未能解决你的问题,请参考以下文章
C++ 中类的默认成员函数的问题(构造函数、析构函数、运算符 =、复制构造函数)(默认 ctor、dtor、复制 ctor)