EMCPP条款11:优先选用删除函数,而非private未定义函数

YiQi 管理员

如果你写了代码给其他程序员用,并且你想阻止他们调用某个特定函数的话,那你只需不要声明该函数即可。函数未经声明,不可调用。易如反掌。但是有时 C++ 会替你声明一些特殊函数。目前,我们只需考虑复制构造函数和复制赋值运算符。

C++98 中为了阻止这些泊数被使用,采取的做法是声明其为 private, 并且不去定义它们。在 C++11 中,有更好的途径来达成效果上相同的结果:使用 = delete 将复制构造函数和复制赋值运算符标识为删除函数 (deleted function) 。

习惯上, 删除由数会被声明为 public, 而非 private,因为把新函数声明成 public, 会得到较好的错误信息。

删除函数的一个重要优点在于,任何函数都能成为删除函数,但只有成员函数能声明成 private。举例来说,假定我们有一个非成员函数,取用一个整数,并返回其是否是个幸运数:

1
bool isLucky(int number);

C++的 C 渊源决定了把可以凑合看作是数值的型别,都可以隐式转型到 int, 但有些调用尽管可以编译,但语义上却了无意义:

1
2
3
if (isLucky('a')) … // is 'a' a lucky number?
if (isLucky(true)) … // is "true"?
if (isLucky(3.5)) … // should we truncate to 3 before checking for luckiness?

而有一个办法就是为我们想要滤掉的型别创建删除重载版本:

1
2
3
4
bool isLucky(int number); // original function
bool isLucky(char) = delete; // reject chars
bool isLucky(bool) = delete; // reject bools
bool isLucky(double) = delete; // reject doubles and floats

还有一个妙处是删除函数能做到而 private 成员函数做不到的,那就是阻止那些不应该进行的模板具现。

1
2
template<typename T>
void processPointer(T* ptr);

如果不可以使用 void*char* 来调用 processPointer:

1
2
3
4
template<>
void processPointer<void>(void*) = delete;
template<>
void processPointer<char>(char*) = delete;

如果是类内部的函数模板,并且你想通过 private 声明来禁用某些具现 (C++98 中的老生常谈),这是做不到的,因为你不可能给予成员函数模板的某个特化以不同于主模板的访问层级。这个毛病在删除函数身上压根就不会表现出来,因为一来它们根本不需要不同的访问层级, 二来也因为成员函数模板可以在类外(即名字空间作用域)被删除:

1
2
3
4
5
6
7
8
9
10
11
class Widget {
public:

template<typename T>
void processPointer(T* ptr)
{ … }

};
template<>
void Widget::processPointer<void>(void*) = delete;
// still public, but deleted

要点速记

  • 优先选用删除函数,而非 private 未定义函数。
  • 任何函数都可以删除,包括非成员函数和模板具现。
此页目录
EMCPP条款11:优先选用删除函数,而非private未定义函数