使用已删除或非用户提供的私有析构函数构造(但不是析构)class 的对象

Constructing (but not destucting) an object of a class with a deleted or non-user-provided private destructor

下面的代码片段格式是否正确?

struct A { ~A() = delete; };
A *pa{new A{}};

class B { ~B() = default; };
B *pb{new B{}};

乍一看,A 的已删除 dtor 和 B 的私有显式默认 dtor 似乎从未使用过(故意内存泄漏,如果你愿意的话),这可以说它是良构的。

Clang 接受各种编译器和 C++ 版本(C++11 到 C++2a)的程序。

另一方面,GCC 拒绝各种编译器和 C++ 版本的程序。

struct A { ~A() = delete; };
A *pa{new A{}};  // GCC error: use of deleted function 'A::~A()'

class B { ~B() = default; };
B *pb{new B{}};  // GCC error: 'B::~B()' is private within this context

(如果格式正确;在我提交错误报告之前:是否有任何针对此极端情况的公开 GCC 错误报告?我自己搜索了 GCC:s bugzilla 无济于事.)


特别是 GCC 接受私有用户提供析构函数的情况:

class C { ~C(); };
C *pc{new C{}};  // OK

class D { ~D() {} };
D *pd{new D{}};  // OK

这可能暗示某些 GCC 对聚合 类 做了一些特殊的事情,因为 AB 是聚合而 CD 不是.但是 GCC 与此行为不一致,如下例

struct E {
    ~E() = delete; 
private: 
    int x;
};
E *pe{new E{}};

where E is not an aggregate (private data member) is likely rejected like for the aggregate 类 A and B above, where the examples of下面的 FG(分别是 C++20 之前的聚合和非聚合)

struct F {
    F() = default;
    ~F() = delete; 
};
F *pf{new F{}};

struct G {
    G() = default;
    ~G() = delete; 
private: 
    int x;
};
G *pg{new G{}};

都被 GCC 接受。

片段格式正确;这是一个 GCC 错误 (59238)

首先,[class.dtor]/4 明确提到可以删除给定 class 的 选定析构函数

At the end of the definition of a class, overload resolution is performed among the prospective destructors declared in that class with an empty argument list to select the destructor for the class, also known as the selected destructor. [...] Destructor selection does not constitute a reference to, or odr-use ([basic.def.odr]) of, the selected destructor, and in particular, the selected destructor may be deleted ([dcl.fct.def.delete]).

[class.dtor]/15 控制隐式调用析构函数的情况;从第一部分开始:

A destructor is invoked implicitly

  • (15.1) for a constructed object with static storage duration ([basic.stc.static]) at program termination ([basic.start.term]),
  • (15.2) for a constructed object with thread storage duration ([basic.stc.thread]) at thread exit,
  • (15.3) for a constructed object with automatic storage duration ([basic.stc.auto]) when the block in which an object is created exits ([stmt.dcl]),
  • (15.4) for a constructed temporary object when its lifetime ends ([conv.rval], [class.temporary]).

[...] A destructor may also be invoked implicitly through use of a delete-expression ([expr.delete]) for a constructed object allocated by a new-expression ([expr.new]); the context of the invocation is the delete-expression.

(15.1)(15.4)

None 适用于此处,特别是“对于由new-expression",这里确实适用,析构函数仅通过使用 delete-expression 隐式调用,而我们不是在这个例子中使用。

[class.dtor]/15 的第二部分涵盖何时 可能调用 析构函数,以及如果可能调用并删除析构函数,则程序格式错误:

A destructor can also be invoked explicitly. A destructor is potentially invoked if it is invoked or as specified in [expr.new], [stmt.return], [dcl.init.aggr], [class.base.init], and [except.throw]. A program is ill-formed if a destructor that is potentially invoked is deleted or not accessible from the context of the invocation.

在这种情况下没有显式调用析构函数。

[expr.new] (particularly referring to [expr.new]/24) 在这里不适用,因为它仅与创建 class 类型的对象数组有关(使用 new-expression ).

[stmt.return] 不适用于此处,因为它涉及(可能)在 return 语句中调用构造函数和析构函数。

[dcl.init.aggr] (particularly [dcl.init.aggr]/8) 不适用于此处,因为它涉及聚合的 元素 的潜在调用的析构函数,而不是聚合的潜在调用的析构函数 class本身。

[class.base.init] 不适用于此处,因为它涉及到 (base class) 子对象的潜在调用析构函数。

[except.throw] (particularly [except.throw]/3 and [except.throw]/5) 不适用于此处,因为它与 异常对象 .

的潜在调用析构函数相关

因此,[class.dtor]/15的none适用于这种情况,GCC拒绝AB和[=的例子是错误的13=] 在 OP 中。正如 所指出的,这看起来像以下打开的 GCC 错误报告:

而且我们可能会注意到,正如错误报告中指出的那样,GCC 在使用列表初始化进行分配时只会错误地拒绝这些程序,而以下修改的示例 ABE 都被 GCC 接受:

struct A { ~A() = delete; };
A *pa{new A()};

class B { ~B() = default; };
B *pb{new B()};

struct E {
    ~E() = delete; 
private: 
    int x;
};
E *pe{new E()};