自动生成默认/复制/移动 ctor 和复制/移动分配运算符的条件?

Conditions for automatic generation of default/copy/move ctor and copy/move assignment operator?

提问人:oompahloompah 提问时间:2/9/2011 最后编辑:Communityoompahloompah 更新时间:11/15/2018 访问量:45695

问:

我想刷新一下编译器通常自动生成默认构造函数、复制构造函数和赋值运算符的条件。

我记得有一些规则,但我不记得了,也在网上找不到信誉良好的资源。谁能帮忙?

C++ 复制构造函数 default-constructor move-constructor move-assignment-operator

评论


答:

158赞 Philipp 2/9/2011 #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),则会自动生成移动赋值运算符。

评论

14赞 kamilk 7/6/2014
继承的析构函数算不算?我的意思是,假设我有一个带有空虚拟析构函数的基类。它是否阻止在子类中创建移动构造函数?如果答案是肯定的,如果我在基类中定义一个移动构造函数会有所帮助吗?
13赞 nonsensickle 8/1/2014
我认为你应该提到,也许在类中拥有成员会阻止构造函数被自动生成......const
1赞 Konstantin Burlachenko 10/12/2016
我知道在这个论坛中发送超链接是有限制的。但这也是一篇好文章——cplusplus.com/articles/y8hv0pDG
1赞 sigy 3/29/2017
请注意,从标准开始,隐式默认的复制构造函数“如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用”(12.8 复制和移动类对象 [class.copy])。
2赞 kamilk 5/4/2022
@John恐怕不是。 8年前是我最后一次与C++合作。
137赞 Marco M. 7/8/2016 #2

我发现下面的图表非常有用。

C++ rules for automatic constructors and assignment operators来自 Sticky Bits - 成为零英雄的规则

评论

1赞 towi 9/27/2016
美丽。“独立”指的是什么?独立于什么?
11赞 Marco M. 10/4/2016
复制 ctor/assignment 彼此“独立”。如果只编写一个,编译器将提供另一个。相反,如果提供移动 ctor 或移动赋值,则编译器不会提供另一个。
1赞 RaGa__M 7/5/2017
想知道复制操作独立背后的原因是什么。历史原因可能是?或者复制不会修改它的目标,但移动会?
1赞 atablash 4/1/2018
@Explorer_N 是的,向后兼容,所以历史原因。这在很久以前是一个糟糕的设计选择,所以现在需要像“三法则”这样的良好实践(定义所有 3 个或不定义:复制构造函数、复制赋值运算符,通常是析构函数)以避免难以发现的错误。
2赞 Enlico 5/19/2019
@MarcoM.,据我所知,“如果你写......”条件包括将特殊成员函数设置为(明显)或(对我来说不太明显)的两种情况。我说得对吗?= delete= default
8赞 Ciro Santilli OurBigBook.com 11/15/2018 #3

C++17 N4659标准草案

有关快速交叉标准参考,请查看以下 cppreference 条目的“隐式声明”部分:

当然,可以从标准中获得相同的信息。例如,在 C++17 N4659 标准草案中:

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)。后者 如果类具有用户声明的复制构造函数或用户声明的析构函数,则不推荐使用案例。

对于移动分配:

4 如果类 X 的定义没有显式声明移动赋值运算符,则隐式 当且仅当

  • (4.1) — X 没有用户声明的复制构造函数,
  • (4.2) — X 没有用户声明的移动构造函数,
  • (4.3) — X 没有用户声明的复制分配运算符,并且
  • (4.4) — X 没有用户声明的析构函数。

15.4 “析构函数”对析构函数是这样说的:

4 如果一个类没有用户声明的析构函数,则析构函数将隐式声明为默认值 (11.4)。一 隐式声明的析构函数是其类的内联公共成员。