自动生成默认/复制/移动构造函数和复制/移动赋值运算符的条件是什么?

153

我希望能够重新了解编译器通常在哪些情况下会自动生成默认构造函数、复制构造函数和赋值运算符。

我记得有一些规则,但是我不记得了,而且在网上也找不到可靠的资源。有人可以帮助吗?

3个回答

158
在下文中,“自动生成”的意思是“被隐式声明为默认,但未定义为删除”。有些情况下,特殊成员函数会被声明,但却被定义为已删除。
  • 如果没有用户声明的构造函数(§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
13
我认为你应该提到,将const成员放入类中会防止自动生成构造函数... - nonsensickle
1
我知道在这个论坛里发送超链接是受限制的。但这也是一篇好文章 - http://www.cplusplus.com/articles/y8hv0pDG/ - Konstantin Burlachenko
1
请注意,根据标准,如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则隐式默认的复制构造函数已被弃用(**12.8 复制和移动类对象 [class.copy]**)。 - sigy
2
@John,恐怕不行。大约8年前是我最后一次使用C++的时间。 - kamilk
显示剩余4条评论

137

1
美好。"独立"指的是什么?独立于什么之外? - towi
11
复制构造函数和复制赋值函数是相互独立的。如果你只写了其中一个,编译器会提供另一个。相比之下,如果你提供了移动构造函数或移动赋值函数中的任一个,编译器就不会提供另一个。 - Marco M.
1
想知道复制操作独立的原因是什么。是否是历史原因?还是因为复制不会修改其目标,而移动会修改? - RaGa__M
1
@Explorer_N 是的,为了向后兼容性和历史原因。很久以前这是一个糟糕的设计选择,所以现在需要好的实践方法,比如“三法则”(定义全部三个或不定义:拷贝构造函数、拷贝赋值运算符和通常的析构函数),以避免难以发现的错误。 - atablash
2
@MarcoM.,据我理解,“如果你写...”的条件包括将特殊成员函数设置为= delete(显而易见)或= default(对我来说不太明显)的两种情况。我是对的吗? - Enlico
显示剩余2条评论

8

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)。如果类具有用户声明的复制构造函数或用户声明的析构函数,则后一种情况已弃用。

对于移动赋值运算符:

如果类X的定义没有显式声明移动赋值运算符,则只有在以下情况下才会隐式声明默认移动赋值运算符:
(4.1)- X没有用户声明的复制构造函数,
(4.2)- X没有用户声明的移动构造函数,
(4.3)- X没有用户声明的复制赋值运算符,并且
(4.4)- X没有用户声明的析构函数。
15.4“析构函数”对析构函数进行了说明:
如果一个类没有用户声明的析构函数,则析构函数将被隐式声明为默认(11.4)。 隐式声明的析构函数是其类的内联公共成员。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接