delete 是唯一能从语言层面彻底禁用重载函数的方式,它在编译期报错而非屏蔽,必须声明于头文件中、不可加函数体或 default,适用于阻止非法调用、隐式转换及完善类接口设计。

用 delete 禁用特定重载函数,不是“屏蔽”,而是编译期报错
想阻止用户对某个类型调用 operator+、std::vector::push_back 或自定义类的构造函数?delete 是唯一能从语言层面彻底禁用重载的方式。它不靠文档提醒,也不靠运行时 assert,而是在编译阶段让非法调用直接失败,错误信息明确指向被删除的函数签名。
关键点在于:必须写在函数声明处(头文件中),且只能用于函数声明,不能用于定义;delete 后不能跟函数体,也不能加 = default。
-
void* operator new(size_t) = delete;—— 禁用裸 new,强制用工厂或智能指针 -
MyClass(const MyClass&) = delete;—— 禁止拷贝(移动仍可启用) -
void process(int) = delete;和void process(double)共存时,传5会编译失败,但传5.0正常
禁用隐式类型转换:重点拦住 int → bool、const char* → std::string 这类“安静的坑”
很多误用源于编译器悄悄做了你没意识到的转换。比如函数接受 bool,结果用户传了 int x = 0;,它被转成 false 还不报错;或者接受 std::string,用户传了字符串字面量,触发临时对象构造——这些都可能掩盖逻辑错误。
用 delete 拦住非预期转换,比事后调试高效得多:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
- 禁
int到bool:MyClass(int) = delete;,只保留MyClass(bool) - 禁裸 C 字符串到包装类:
StringWrapper(const char*) = delete;,只留StringWrapper(std::string_view)或显式构造函数 - 注意:如果同时有
MyClass(T)和MyClass(U),且T可隐转为U,则MyClass(T) = delete;不起作用——因为重载决议会跳过它,选MyClass(U)。此时需连U的版本也删,或改用explicit
delete 和 explicit 的分工:一个管“能不能调”,一个管“要不要自动转”
别混淆两者的职责。explicit 只影响单参数构造函数和类型转换运算符的隐式调用,不影响重载决议本身;delete 是彻底移除该函数参与重载的能力。
- 写
explicit MyClass(int);:允许MyClass m(42);,但禁止func(MyClass); func(42); - 写
MyClass(int) = delete;:连MyClass m(42);都不许,更别说隐式转换 - 常见组合:
explicit MyClass(std::string);+MyClass(const char*) = delete;—— 接受std::string但拒绝 C 字符串,且不自动转换
容易忽略的兼容性细节:模板、继承、SFINAE
模板函数也能 delete,但行为比普通函数微妙。被 delete 的模板特化仍参与重载决议,只是匹配后才报错——这和 SFINAE 冲突,可能导致本该退到其他重载的场景直接失败。
-
template<typename t> void foo(T) = delete;</typename>—— 封死所有类型,但foo<int>(42)</int>报错位置不如非模板直观 - 派生类中
delete基类函数:无法真正“删除”继承来的函数,只能隐藏(hiding),且不会阻止通过基类指针调用。真要禁用,得在基类里delete - 和
auto返回类型、constexpr一起用没问题,但别对operator=等特殊成员函数仅用delete而不考虑移动语义完整性
最易漏的是:当多个重载都带 delete,编译器报错时只提示“candidate function has been explicitly deleted”,不会列出所有被删的候选。得自己翻头文件确认删对了哪几个签名。

















