使用已删除或非用户提供的私有析构函数构造(但不是析构)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 对聚合 类 做了一些特殊的事情,因为 A
和 B
是聚合而 C
和 D
不是.但是 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下面的 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 明确提到可以删除给定 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拒绝A
、B
和[=的例子是错误的13=] 在 OP 中。正如 所指出的,这看起来像以下打开的 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()};
下面的代码片段格式是否正确?
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 对聚合 类 做了一些特殊的事情,因为 A
和 B
是聚合而 C
和 D
不是.但是 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下面的 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 明确提到可以删除给定 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 控制隐式调用析构函数的情况;从第一部分开始:
(15.1) 到 (15.4) 的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.
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拒绝A
、B
和[=的例子是错误的13=] 在 OP 中。正如
而且我们可能会注意到,正如错误报告中指出的那样,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()};