C++ 为什么对未删除的对象调用析构函数?

C++ 为什么对未删除的对象调用析构函数?,c++,language-lawyer,C++,Language Lawyer,无法编译,错误消息为: 错误:使用已删除的函数“A::~A()” 新的{} 据我所知,我并没有破坏这个对象,为什么它要调用析构函数呢 编撰 通用条款8.1.0 struct A { ~A() = delete; }; int main() { new A{}; } 据我所知,示例中没有对象被破坏,如果表达式被更改为newa,它会被编译 我认为未编译的示例代码是GCC中的一个bug 回答新添加的语言标记 关键的标准规则是在[class.dtor]中: 析构函数被隐式调用 。。

无法编译,错误消息为:

错误:使用已删除的函数“A::~A()” 新的{}

据我所知,我并没有破坏这个对象,为什么它要调用析构函数呢

编撰 通用条款8.1.0

struct A
{
    ~A() = delete;
};

int main()
{
    new A{};
}

据我所知,示例中没有对象被破坏,如果表达式被更改为
newa,它会被编译

我认为未编译的示例代码是GCC中的一个bug


回答新添加的语言标记

关键的标准规则是在[class.dtor]中:

析构函数被隐式调用

。。。不适用于动态存储以外的其他存储持续时间的情况

。。。破坏者 还通过使用delete表达式(5.3.5)隐式调用 一个新的表达式(5.3.4);调用的上下文是delete表达式。[注意:类的数组 类型包含多个子对象,每个子对象调用析构函数。-end note]析构函数可以 也可以显式调用。如果调用析构函数或按照5.3.4、12.6.2中的规定调用析构函数,则可能会调用析构函数, 和15.1

5.3.4为[expr.new],仅规定

。。。如果 新表达式创建类类型的对象数组,析构函数可能被调用(12.4)

这不适用

12.6.2是[class.base.init],它只指定

在非委托构造函数中,类类型的每个可能构造的子对象的析构函数是 可能被调用(12.4)

这不适用

15.1是[except.throw],它指定如何销毁异常对象,但不适用


结论:第5.3.4节、第12.6.2节和第15.1节均未提及。包含适用于这种情况的规则,并且不会调用析构函数,也没有删除表达式。因此,不会调用析构函数,因此删除析构函数的格式良好。

这里可能有gcc错误

该标准指定在新表达式创建数组时可能调用析构函数:

如果新表达式创建了类类型的对象或对象数组,则对分配函数、解除分配函数和构造函数进行访问和模糊控制。 如果新表达式创建类类型的对象数组,则可能会调用析构函数。

重点矿山

gcc在创建非数组时也应用此规则,而非标准规则。由于下面的注释,gcc的作用似乎正好相反:在创建非数组时,它认为析构函数可能被调用,而在创建数组时,它只是不检查析构函数。

这是正确的


让我们从头开始

:

隐式或显式引用已删除函数而不是声明它的程序是格式错误的

显然,我们并没有明确地提到
~A()
。我们是在暗指它吗

析构函数被隐式调用

  • 对于在程序终止([basic.start.term])时具有静态存储持续时间([basic.stc.static])的构造对象
  • 对于线程出口处具有线程存储持续时间([basic.stc.thread])的构造对象
  • 对于在其中创建对象的块退出([stmt.dcl])时具有自动存储持续时间([basic.stc.auto])的构造对象
  • 对于构造的临时对象,当其生存期结束时([conv.rval],[class.temporary])
或在:

如果新表达式创建类类型的对象数组,则可能会调用析构函数

我们有这些东西吗?不,这里没有具有自动、静态或线程存储持续时间的对象,也没有构造的临时对象,我们的新表达式也没有创建数组。这里只有一个对象,就是我们正在初始化的具有动态存储持续时间的
A


因为我们既不是显式地也不是隐式地引用
~A()
,所以我们不能被这条规则绊倒。因此,gcc错误。还要注意,gcc接受
新的
新A()
,就本规则而言具有相同的含义。

没有足够的
语言回答律师
,抱歉。您需要添加更多的标准代码,使其成为此标记中的正确答案。@SergeyA我已从[class.dtor]中查找了我认为最相关的标准规则。从技术上讲,引用的规则并不表示示例程序的格式不是错误的。这似乎是一个很好的提示,说明了为什么会出现此错误。@richarcrited,在本例中,clang拒绝代码:。也许只是一个额外的!gcc代码中的字符!!是的,看起来像个bug新的a[10]
编译的事实令人费解,因为如果
a()
抛出怎么办?本例中生成的代码假装不需要调用析构函数,这是不正确的。看起来很像gcc中的一个bug。使用a()抛出:因为
a
是否不需要析构函数?将
A
设为非POD会产生预期的错误:根据标准,当其中一个元素抛出构造函数(在其他答案中详细讨论)时,您错过了为数组元素调用析构函数的案例@SergeyA,即使构造函数不例外,析构函数也可能被调用。@Oliv,当然,你回答得很准确。我只是向巴里展示一个(简化的)案例。@巴里,很公平,你指的是一个具体案例。具有讽刺意味的是,它看起来可能是相关的:)@Barry另一方面,既不是静态存储持续时间,也不是线程存储持续时间,。。。是在这里创建的!!评论不用于扩展讨论;这段对话已经结束。请注意。这个问题中的例子并不实际,因为它是错误的
g++ -std=c++17 -O2