以下代码片段格式正确吗?
struct A { ~A() = delete; };
A *pa{new A{}};
class B { ~B() = default; };
B *pb{new B{}};
乍一看,似乎 A
的已删除 dtor和 B
的私有(private)显式默认 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 接受用户提供的私有(private)析构函数的情况:
class C { ~C(); };
C *pc{new C{}}; // OK
class D { ~D() {} };
D *pd{new D{}}; // OK
这可能暗示某些 GCC 为聚合类做一些特殊的事情,如 A
和 B
是聚合,而 C
和 D
不是。但是 GCC 与此行为不一致,如下例所示struct E {
~E() = delete;
private:
int x;
};
E *pe{new E{}};
在哪里 E
不是聚合(私有(private)数据成员)同样被拒绝,如聚合类 A
和 B
上面,而 F
的示例和 G
下面(分别是 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明确提到可以删除给定类的选定析构函数:
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) 中的任何一个都不适用于此处,尤其是“对于由 new 表达式分配的构造对象”,它确实适用于此处,仅通过使用删除表达式隐式调用析构函数,我们是在这个例子中没有使用。
[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] (特别是指 [expr.new]/24 )不适用于此处,因为它仅与创建类类型对象数组时有关(使用新表达式)。
[stmt.return]不适用于此处,因为它涉及(可能)在 return 语句中调用构造函数和析构函数。
[dcl.init.aggr] (特别是 [dcl.init.aggr]/8 )在这里不适用,因为它与聚合元素的潜在调用析构函数有关,而不是聚合类本身的潜在调用析构函数。
[class.base.init]不适用于此处,因为它与(基类)子对象的潜在调用析构函数有关。
[except.throw] (尤其是 [except.throw]/3 和 [except.throw]/5 )在这里不适用,因为它与异常对象的潜在调用析构函数有关。
因此,[class.dtor]/15 在这种情况下都不适用,GCC 拒绝
A
的示例是错误的。 , B
和 E
在OP中。正如所指出的 in a comment by @JeffGarrett ,这看起来像以下打开的 GCC 错误报告:我们可能会注意到,正如错误报告中指出的那样,GCC 在使用列表初始化分配时只会错误地拒绝这些程序,而以下修改示例
A
, B
和 E
都被 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()};
关于c++ - 构造(但不破坏)具有已删除或非用户提供的私有(private)析构函数的类的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65124761/