删除析构函数后,编译器是否仍会隐式生成默认构造函数?
删除析构函数后,编译器是否仍会隐式生成默认构造函数?
问题场景
当类中显式删除析构函数时,会出现看似矛盾的两种现象:
- GCC允许编译以下代码:
struct A { ~A() = delete; int x; int y; virtual void foo() { } }; int main() { A *pmc = new A; // GCC允许编译,但会导致内存泄漏 return 0; }
从生成的汇编代码能看到,GCC确实隐式生成了默认构造函数:
A::A() [base object constructor]: push rbp mov rbp, rsp mov QWORD PTR [rbp-8], rdi mov edx, OFFSET FLAT:vtable for A+16 mov rax, QWORD PTR [rbp-8] mov QWORD PTR [rax], rdx nop pop rbp ret
- 但通过
is_default_constructibletrait检测时,结果为false:
cout << is_default_constructible<A>::value; // 输出false
核心疑问与解答
很多人会疑惑:is_default_constructible的结果是否代表C++标准的态度?是不是标准层面不允许生成默认构造函数,但编译器实现层面违规允许?
这种理解是错误的,两者的行为都符合C++标准,只是关注的维度不同:
- 默认构造函数的生成:根据C++标准,只要类没有显式声明任何构造函数,即使显式删除了析构函数,编译器仍然会隐式生成默认构造函数。GCC生成该构造函数的行为完全符合标准。
is_default_constructible的含义:这个type trait检测的不是“默认构造函数是否存在”,而是“是否可以合法执行T t;这种默认构造表达式”。由于A的析构函数被删除,该类型是不可销毁的——标准要求,默认构造一个对象的语境需要确保对象能被正常销毁(除非是像new A这种永远不销毁的极端场景),因此is_default_constructible<A>::value返回false,这也符合标准对该trait的定义。- GCC允许
new A的原因:new表达式只负责分配内存并构造对象,不要求立即销毁对象。标准并没有禁止构造一个不可销毁的对象,只是禁止在需要销毁它的语境下使用(比如栈上声明A a;就会编译失败),因此GCC的行为是合规的。
总结
- 编译器会隐式生成默认构造函数,符合标准规定;
is_default_constructible返回false是因为该类型无法被正常销毁,符合标准对该trait的定义;- 两者的行为并不矛盾,只是分别对应“构造函数是否存在”和“默认构造表达式是否合法”两个不同的标准规则。
内容的提问来源于stack exchange,提问作者Yue Zhou
相关产品推荐
相关产品推荐

