删除函数 - C++中禁止特定函数调用的方法

在某些情况下,我们编写的函数在被特定类型的实参调用时,行为可能并不符合预期。

考虑以下示例:

#include <iostream>

void printInt(int x)
{
    std::cout << x << '\n';
}

int main()
{
    printInt(5);    // 正确:输出 5
    printInt('a');  // 输出 97 —— 是否合理?
    printInt(true); // 输出 1 —— 是否合理?

    return 0;
}

程序输出:

5
97
1

显然 printInt(5) 没有问题,但后两次调用值得质疑。编译器会把 'a' 提升为 int97,把 true 提升为 int1,并静默完成匹配。

假设我们认为用 charbool 调用 printInt() 并不合理,应当如何处理?

使用 = delete 删除函数

若我们明确禁止某函数被调用,可用 = delete 说明符将该函数标记为“已删除”。一旦编译器在重载决议中选中了被删除的函数,编译将立即中止并报错。

更新后的示例如下:

#include <iostream>

void printInt(int x)
{
    std::cout << x << '\n';
}

void printInt(char)  = delete; // 禁止 char 版本
void printInt(bool)  = delete; // 禁止 bool 版本

int main()
{
    printInt(97);   // 正确
    printInt('a');  // 编译错误:函数已删除
    printInt(true); // 编译错误:函数已删除
    printInt(5.0);  // 编译错误:存在二义性
    return 0;
}

逐条分析:

  • printInt('a') 精确匹配 printInt(char),但该版本已被删除 → 编译错误。
  • printInt(true) 精确匹配 printInt(bool),同样已删除 → 编译错误。
  • printInt(5.0) 的解析过程稍复杂:
    1. 不存在 printInt(double) 的精确匹配;
    2. 剩余候选为 printInt(int)printInt(char) = deleteprintInt(bool) = delete
    3. 由于被删除函数仍参与重载决议,且 double → intdouble → chardouble → bool 均为数值转换,没有哪一个转换优于其他,编译器报二义性(ambiguous match)错误。

关键洞察

= delete 的含义是“我禁止此重载”,而非“此函数不存在”。被删除的函数在重载决议的所有阶段均参与比较;一旦被选中,即触发编译错误。

供进阶读者

其他类别的函数(成员函数、函数模板特化等)也可使用 = delete

  • 成员函数的删除见 14.14 课 —— 拷贝构造函数简介
  • 函数模板特化的删除见 11.7 课 —— 函数模板实例化

高级技巧:一次性删除所有非精确匹配

逐条删除各个重载可行但繁琐。若希望某函数接受与形参完全匹配的实参,可借助函数模板(将在 11.6 课 —— 函数模板中介绍)实现:

#include <iostream>

// 精确匹配 int 时使用
void printInt(int x)
{
    std::cout << x << '\n';
}

// 对所有其他类型使用已删除的模板
template <typename T>
void printInt(T x) = delete;

int main()
{
    printInt(97);   // 正确
    printInt('a');  // 编译错误
    printInt(true); // 编译错误
    return 0;
}

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"