A() = default; 保持类型平凡性、触发值初始化、维持聚合资格,而 A() {} 破坏平凡性、不初始化成员、丧失聚合与ABI兼容性。

构造函数写 A() {} 会让类型失去平凡性
编译器看到 A() {},立刻认定这是“用户提供的构造函数”,哪怕函数体为空。它会取消该类型的“平凡类型(trivial type)”资格,导致 std::is_trivial_v<a></a> 返回 false。这意味着:不能用 memcpy 安全复制、不能用 memset 清零、STL 容器可能退化为逐个调用构造/析构、甚至影响结构体的 ABI 兼容性。
而 A() = default; 明确告诉编译器:“我要的就是编译器原生默认行为”,只要成员都支持,默认构造就是平凡的,std::is_trivial_v<a></a> 为 true。
- 常见错误现象:
struct LogEntry { uint64_t ts; int level; char msg[256]; LogEntry() {}; };—— 压测时吞吐量骤降,改用= default立刻恢复 - 使用场景:高性能日志、网络协议包、内存池管理等对拷贝/初始化开销敏感的代码
- 性能影响:平凡类型可触发 memcpy 优化;非平凡类型强制走构造函数调用路径,开销可能高一个数量级
=default 能恢复被抑制的隐式默认构造函数
一旦你写了任何其他构造函数(比如 A(int)),编译器就不再生成隐式默认构造函数。此时若想同时支持 A() 和 A(42),必须显式提供无参版本。
手写 A() {} 虽然能编译通过,但如前所述,会破坏平凡性;而 A() = default; 不仅补上了接口,还保住了类型语义。
立即学习“C++免费学习笔记(深入)”;
- 参数差异:两者都接受零参数,但
=default的函数具有noexcept、constexpr(若成员支持)等隐含属性,手写空函数没有 - 容易踩的坑:在类中定义了带参构造后,忘记加
=default,导致std::vector<a>(10)</a>编译失败(需要默认构造) - 兼容性影响:C++17 起,聚合类型要求默认构造函数是
=default或不存在;手写空函数会让类型失去聚合资格
值初始化行为完全不同
对栈上对象 A a{}; 或堆上 new A{},两种写法触发的初始化逻辑不同:
-
A() {}:执行你写的空函数体,成员保持未初始化(内置类型如int x;是垃圾值) -
A() = default;:触发“值初始化(value-initialization)”,对内置类型做零初始化(x变成0),对类类型调用其默认构造
这个差异在调试和安全编码中很关键——手写空构造函数常导致未定义行为,而 =default 更接近“什么都没写”的原始语义。
=delete 和手写 private 构造函数不是一回事
虽然标题问的是 =default vs 手写空函数,但实践中常有人混淆 =delete 和旧式 private 禁用方式。这里顺带点明:用 A() = delete; 禁用默认构造,会在任何调用点(包括类内成员函数)触发编译期错误,提示 “call to deleted function”;而 private: A(); 在友元或成员函数里仍可能被误调,且错误常延迟到链接阶段。
真正难察觉的复杂点在于:平凡性、值初始化、聚合判定、constexpr 推导这四条线是联动的。改一个 {} 成 =default,可能同时修复多个层面的问题,而不是只解决“能不能编译”。


















