struct A
{
~A() = delete;
};
int main()
{
new A{};
}
这无法编译并显示错误消息:
错误:使用已删除的函数'A ::〜A()' 新的A {};
据我了解,我并没有销毁该对象,为什么它要尝试调用析构函数?
编译为 GCC 8.1.0
g++ -std=c++17 -O2
答案 0 :(得分:37)
让我们从头开始。
除声明其内容外,隐式或显式引用已删除函数的程序格式错误。
很显然,我们不是在明确引用~A()
。我们是否暗指它? [class.dtor]/12:
析构函数被隐式调用
- 对于在程序终止([basic.start.term])具有静态存储持续时间([basic.stc.static])的构造对象,
- 对于在线程出口具有线程存储持续时间([basic.stc.thread])的构造对象,
- 对于在创建对象的块退出([stmt.dcl])时具有自动存储持续时间([basic.stc.auto])的构造对象,
- 用于构造的临时对象的生命周期结束时([conv.rval],[class.temporary])。
或在[expr.new]/20中:
如果 new-expression 创建一个类类型的对象数组,则可能会调用析构函数。
我们有这些东西吗?不,这里没有对象具有自动,静态或线程存储持续时间,也没有构造的临时对象,我们的 new-expression 也没有创建数组。此处只有一个对象,一个A
,具有动态存储持续时间,我们正在对其进行初始化。
由于我们既没有明确地也没有隐含地指称~A()
,所以我们不能违反该规则。因此,gcc错误。另请注意,gcc接受new A;
和new A();
,就此规则而言,它们具有相同的含义。
答案 1 :(得分:4)
这里可能是gcc错误。
该标准指定在新表达式创建数组[expr.new]时潜在地调用析构函数:
如果new表达式创建一个类类型的对象或对象数组,则将对分配函数,解除分配函数和构造函数进行访问和模糊控制。 如果new表达式创建了一个类类型的对象数组,则可能会调用析构函数。
强调我的
gcc在创建非数组时也适用此规则,这显然不是标准规则。由于以下注释,gcc似乎完全相反:在创建非数组时,它考虑了析构函数被潜在地调用,并且在创建数组时只是不检查析构函数。
答案 2 :(得分:2)
据我所知,该示例中没有破坏任何对象,并且如果将表达式更改为new A;
我认为未编译的示例代码是GCC中的错误。 Clang compiles it just fine。
回答新添加的language-lawyer标签。
关键标准规则在[class.dtor]中是这样的:
析构函数被隐式调用
...不适用的情况涉及动态存储以外的其他存储持续时间...
...破坏者 对于由分配的构造对象,还可以通过使用delete-expression(5.3.5)隐式调用 一个new表达式(5.3.4);调用的上下文是delete-expression。 [注意:类的数组 type包含几个子对象,每个子对象都会调用析构函数。 —尾注]析构函数可以 也可以显式调用。如果析构函数被调用或按照5.3.4、12.6.2, 和15.1。
5.3.4是[expr.new],仅指定
...如果 new-expression创建一个类类型为对象的数组,可能会调用析构函数(12.4)。
这不适用。
12.6.2是[class.base.init],仅指定
在非委托构造函数中,类类型的每个可能构造的子对象的析构函数为 可能被调用(12.4)。
哪些不适用
15.1是[except.throw],它指定如何销毁异常对象,该对象不适用
结论:第5.3.4、12.6.2和15.1节均未涉及。包含适用于这种情况的规则,并且不会调用析构函数,也没有delete-expression。因此,析构函数不会被调用,因此可以很好地删除析构函数。