代码之家  ›  专栏  ›  技术社区  ›  PoweredByRice

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

  •  58
  • PoweredByRice  · 技术社区  · 7 年前
    struct A
    {
        ~A() = delete;
    };
    
    int main()
    {
        new A{};
    }
    

    编译失败,错误消息为:

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

    编译时使用 合同一般条件8.1.0

    g++ -std=c++17 -O2
    
    3 回复  |  直到 7 年前
        1
  •  37
  •   Barry    7 年前

    这是 gcc bug 57082 .


    [dcl.fct.def.delete]/2 :

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

    ~A() 明确地。我们指的是含蓄的吗? [class.dtor]/12 :

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

    或在 [expr.new]/20 :

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

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

    因为我们既没有明示也没有暗示 ~A() ,我们不能被那条规则绊倒。因此,gcc错误。还要注意,gcc接受 new A; new A(); ,在本规则中具有相同的含义。

        2
  •  4
  •   Oliv    7 年前

    可能是gcc的错误。

    标准指定在新表达式创建数组时可能调用析构函数 [expr.new] :

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

    gcc在创建非数组(隐式地不是标准规则)时也应用此规则。

        3
  •  2
  •   eerorika    7 年前

    据我所知,示例中没有对象被破坏,如果将表达式更改为 new A;

    我认为不编译示例代码是GCC中的一个bug。 Clang compiles it just fine


    回答新添加的语言律师标签。

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

    析构函数被隐式调用

    ... 不适用于涉及动态存储时间以外的其他存储时间的情况。。。

    也可以通过使用delete表达式(5.3.5)为 一个新表达式(5.3.4);调用的上下文是delete表达式。[注:类数组 类型包含多个子对象,每个子对象调用析构函数。结束语]析构函数可以 也可以显式调用。如果调用了析构函数或按照5.3.4、12.6.2中的规定,则析构函数可能被调用, 和15.1。

    5.3.4是只指定

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

    12.6.2是只指定

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

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

    结论:没有第5.3.4、12.6.2和15.1节。包含适用于此情况的规则,析构函数不被调用,也不存在delete表达式。因此,析构函数不可能被调用,因此它的格式很好,可以删除析构函数。