defalut

预置函数
显式预置的函数定义,只能用于特殊成员函数和比较运算符函数

特殊成员函数
一些成员函数是特殊的:在某些环境下,即使用户不定义编译器也会定义它们。它们是:

  • 默认构造函数
  • 复制构造函数
  • 移动构造函数
  • 复制赋值运算符
  • 移动赋值运算符
  • 析构函数

特殊成员函数以及比较运算符(C++20 起)是仅有的能被预置的函数,即使用 = default 替代函数体进行定义。

delete

弃置函数
如果使用特殊语法 = delete ;取代函数体,那么该函数被定义为弃置的(deleted)。任何弃置函数的使用都是非良构的(程序无法编译)。这包含调用,包括显式(以函数调用运算符)及隐式(对弃置的重载运算符、特殊成员函数、分配函数等的调用),构成指向弃置函数的指针或成员指针,甚至是在不求值表达式中使用弃置函数。但是可以隐式 ODR 使用刚好被弃置的非纯虚成员函数。

如果函数被重载,那么首先进行重载决议,且只有在选择了弃置函数时程序才非良构:

1
2
3
4
5
6
struct sometype
{
void* operator new(std::size_t) = delete;
void* operator new[](std::size_t) = delete;
};
sometype* p = new sometype; // 错误:尝试调用弃置的 sometype::operator new

函数的弃置定义必须是翻译单元中的首条声明:已经声明过的函数不能声明为弃置的:

1
2
struct sometype { sometype(); };
sometype::sometype() = delete; // 错误:必须在首条声明弃置

由用户提供的函数

如果一个函数由用户声明且没有在它的首个声明被显式预置或显式弃置,那么它由用户提供。由用户提供的显式预置的函数(即在它的首个声明后被显式预置)在它被显式预置的地方定义;如果该函数被隐式定义为弃置的,那么程序非良构。需要为不断变化的代码库提供稳定的二进制接口的情况下,在函数的首个定义后再声明为预置可以保证执行效率,也能提供简明的定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// trivial 的所有特殊成员函数都分别在它们的首个声明处被显式预置,
// 因此它们都不由用户提供
struct trivial
{
trivial() = default;
trivial(const trivial&) = default;
trivial(trivial&&) = default;
trivial& operator=(const trivial&) = default;
trivial& operator=(trivial&&) = default;
~trivial() = default;
};

struct nontrivial
{
nontrivial(); // 首个声明
};

// 没有在首个声明处被显式预置,
// 因此该函数由用户提供并在此定义
nontrivial::nontrivial() = default;