

新闻资讯
技术教程=default用于显式要求编译器生成特殊成员函数的默认实现,=delete用于彻底禁用函数;二者仅适用于构造、析构、拷贝/移动函数,需在类内声明且不可有函数体,组合使用可精准表达资源管理意图。
编译器生成默认实现当类中定义了任意构造函数(比如带参构造),编译器就不会自动生成默认构造函数 MyClass();但有时你仍需要它,又不想手写空实现——这时用 = default 告诉编译器“请按老规矩生成”。它只能用于特殊成员函数:构造函数、析构函数、拷贝/移动构造函数、拷贝/移动赋值运算符。
注意:必须放在类内声明处,且不能有函数体;放在类外定义时,需同时指定 inline(否则链接错误):
class Widget {
public:
Widget() = default; // ✅ 类内,最常用
Widget(const Widget&) = default;
~Widget() = default;
Widget& operator=(const Widget&) = default;
};
常见误用:
= default → 编译错误
Widget::Widget() = default; 却没加 inline → LNK2005 或 undefined reference= default → 不合法,仅限特殊成员函数= delete 是比私有化更彻底的禁用方式。它让函数在编译期就不可调用,连友元和类内代码都不能用。典型用途是禁止拷贝、禁用不安全的类型转换、或封禁特定参数组合的重载。
例如禁用拷贝:
class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
其他实用场景:
int 到类的隐式构造:explicit MyClass(int) = default; 不行,得写 MyClass(int) = delete; 配合其他构造函数nullptr):void process(char*) = delete;
template void foo(T) = delete; ,再特化允许的类型关键点:被 = delete 的函数仍参与重载决议,只是匹配后立即报错 —— 所以它能精准拦截“不该发生的调用”,比运行期断言更早发现问题。
现代 C++ 中二者常配合,表达清晰的资源管理意图。比如实现移动语义但禁用拷贝:
class UniquePtr {
public:
UniquePtr() = default;
UniquePtr(UniquePtr&&) = default; // 允许移动
UniquePtr(const UniquePtr&) = delete; // 禁止拷贝
UniquePtr& operator=(UniquePtr&&) = default;
UniquePtr& operator=(const UniquePtr&) = delete;
};
另一个典型是 PIMPL 模式中,把拷贝操作显式 = delete,而将默认构造、析构设为 = default(即使它们在类外定义,也需加 inline 或在头文件中定义)。
容易忽略的细节:
= default 的函数仍是 public 且 noexcept(如果编译器生成的版本是);= delete 的函数访问权限无关紧要,它根本不会进入调用流程= delete,派生类中同签名函数不会自动继承该删除状态,必须显式再次 = delete
= delete 可用于 SFINAE 替代方案,但要注意它不导致 SFINAE 失败,而是硬错误 —— 如需 SFINAE,请用 enable_if
传统做法是把拷贝构造函数声明为 private 且不定义,但存在明显缺陷:
= delete 在编译期拦截所有调用,错误位置精确到行号,消息明确提示 “call to deleted function”。这是类型安全的关键升级。
真正复杂的点在于:什么时候该用 = default 而不是手写?答案是——当你需要的是“编译器原本会生成的那个行为”,且该行为符合你的语义(如 trivially copyable),就用 = default;一旦涉及自定义逻辑(比如日志、验证、资源预分配),就必须手写。