[TOC]

C++ 默认生成的特殊函数

前言

再度感叹 C++ 历史包袱有点重,为了兼容旧代码导致细节的繁杂。

为了撰文的方便,constructor, assignment operator, destructor 分别由 ctor, assignment, dtor 替代

C++ 11 以前

Rule of Three (Big Three)

Big Three 含义:C++11 前,如果手动定义了以下三个函数之一或者更多,那么应尽可能手动定义所有三个。这只是一个大家都认为比较好的约定准则,并没有被写入标准。

  • copy ctor
  • copy assignment
  • destructor

这三个函数之所以特殊,是因为涉及到 处理资源的生命周期,编译器认为,既然程序员显示定义了某个特殊函数,这表明这个类的资源管理有点复杂(non-trivial),这超出了编译器自动处理能力的范围,程序员理应编写涉及资源管理的其他函数。

C++11 前默认生成的函数

  • default ctor
  • destructor
  • copy ctor
  • copy assignment

These functions are known as the special member functions, and they are what make simple user-defined types in C++ behave like structures do in C.

编译器自动生成这些函数,是为了使得 C++ 里用户自定义的类型能够像 C 里面的类型一样地表现。

比如:

int a = 2; // in c code
TypeA a;
TypeA aa = a; // in C++ code.

值得一提的是 default ctor,default ctor初始化资源,在语义上更接近于任何其他取值的构造函数(也称为 initialization ctor),而不是那些处理资源生命周期的特殊函数。这也是为什么其他三个函数称为 big three。

不好的习惯:尽管有 Rule of Three,但是 C++98 并不强制执行。

C++98 didn’t natively enforce the rule of 3.

具体来说:

  • 如果有自定义了某个函数,那么编译器不会生成对应函数,很容易理解。比如明确定义了一个 ctor,那么就不会自动生成 default ctor。同理 dtor, copy ctor, copy assigment.
  1. 如果自定义了任何 ctor,则不会自动生成 default ctor。
  2. 如果自定义了 dtor,则不会自动生成 default dtor。
  3. 但对于copy function 而言
    • copy ctor: 不论你是否写了 initialization ctor 或者 copy assignment, dtor,都会默认生成 ctor。
    • copy assignment : 不论你是否写了 initialization ctor 或者 copy assignment, dtor,都会默认生成(因为标准不强制执行 Rule of 3)。

言下之意:C++11 前,dtor, copy ctor, copy assignment 只要用户不定义他们,编译器就会自动生成一个。而如果自定义了任何 ctor,则不会自动生成 default ctor。 但是,C++11 后,规则有所改变,见后文。

Although considered good practice, the compiler can’t enforce it. The C++ standard [ N1316 ] mandates that implicit versions will be created if a user doesn’t declare them explicitly:

§ 12.4 / 3 If a class has no user-declared destructor, a destructor is declared implicitly

§ 12.8 / 4 If the class definition does not explicitly declare a copy constructor, one is declared implicitly.

§ 12.8 / 10 If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly

步入 C++11

C++11 引入了 移动语义,加入了 move ctor 和 move assignment。

Rule of Five

Rule of Five: 含义:C++11 以来,如果手动定义了以下五个函数之一或者更多,那么应尽可能手动定义全部五个。

  • copy ctor
  • copy assignment
  • destructor
  • move ctor
  • move assignment

C++11 及其以后 默认生成的函数

C++11 引入了 =default 显示表示编译器默认生成的函数,后文虽把 “标记为=default”、“编译器默认生成” 分成两种说法,是为了易于理解,本质上一回事。

尽管 C++98 没有强制推行 Rule of Three, 而 C++11 又想强制推行 Rule of Five(后面又提倡 Rule of Zero),但是又得考虑兼容以前的旧代码,混乱由此而生。

具体来说:

  • 如果有自定义了某个函数,那么编译器不会生成对应函数,很容易理解。比如明确定义了一个 ctor,那么就不会自动生成 default ctor。同理 dtor, copy ctor, copy assigment.
  1. 如果自定义了任何 ctor,则不会自动生成 default ctor。

  2. 如果自定义了 dtor,则不会自动生成 default dtor。

  3. 对于 copy function(包括 copy ctor 和 copy assignment) 而言,如果没有 move ctor,move assignment ,这种情况下将自动生成 copy function,因为编译器认为这是 C++11 以前的代码,那么为了兼容,就得自动生成 copy ctor 和 copy assignment。具体规则如下:

    • 3.1 对于 copy ctor: 为了兼容,
      • 如果用户没有自定义 copy ctor,先考虑自动生成一个默认的,(后续可能被删除/标记为=delete)
      • 如果用户自定义了 move function,那么将之前生成的标记为=delete;如果没有 move function,标记为 =default
      • 但如果用户自定义了 copy assignment / dtor,则还是会生成一个默认的,但是这个特性是被弃用的,deprecated. 会报 warning,未来这个特性会被删除。
    • 3.2 copy assignment 同理。

    C++11 [ N3242 ] added wording to the Standard, deprecating the previous behavior.

    D.3 Implicit declaration of copy functions [depr.impldec]

    The implicit definition of a copy constructor as defaulted is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor. The implicit definition of a copy assignment operator as defaulted is deprecated if the class has a user-declared copy constructor or a user-declared destructor. In a future revision of this International Standard, these implicit definitions could become deleted

    This means that compilers keep generating a defaulted copy constructor, assignment operator and destructor if no user-defined declaration is found, but at least now they might issue a warning.


    § 12.8 / 7 copy ctor

    If the class definition does not explicitly declare a copy constructor, one is declared implicitly.

    If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.

    § 12.8 / 18 copy assignment

    If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly.

    If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor.

  4. move ctor,只在 Rule of Five 里的其他四个函数没有被自定义的时候自动生成。

  5. move assignment,只在 Rule of Five 里的其他四个函数没有被自定义的时候自动生成。

言下之意:相比 C++11 以前的规则,加入了两个 move function,copy function 只是加了 move function 的限制。两个 move function 则完全遵守 Rule of Five 的规则。

参考

习惯了对网络上的中文资料保持警惕,却偏信了英文资料,细节方面还是得参考权威。Wikipedia, Microsoft Docs, fluent cpp 说法细节上的不一致甚至错误,耗费了太多时间。

  1. https://accu.org/journals/overload/22/120/alday_1896/