在某些情况下,我们编写的函数在被特定类型的实参调用时,行为可能并不符合预期。
考虑以下示例:
#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'
提升为 int
值 97
,把 true
提升为 int
值 1
,并静默完成匹配。
假设我们认为用 char
或 bool
调用 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)
的解析过程稍复杂:- 不存在
printInt(double)
的精确匹配; - 剩余候选为
printInt(int)
、printInt(char) = delete
、printInt(bool) = delete
; - 由于被删除函数仍参与重载决议,且
double → int
、double → char
、double → 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;
}