web-dev-qa-db-ja.com

削除されていないオブジェクトに対してデストラクタが呼び出されるのはなぜですか?

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

int main()
{
    new A{};
}

これはエラーメッセージでコンパイルに失敗します。

エラー:削除された関数 'A ::〜A()' new A {}の使用;

私が理解しているように、私はオブジェクトを破壊しないので、なぜデストラクタを呼び出そうとしているのですか?

GCC 8.1.0でコンパイル

g++ -std=c++17 -O2
59
PoweredByRice

これは gcc bug 57082 です。


下から上へ行きましょう。

[dcl.fct.def.delete]/2

削除された関数を宣言する以外に暗黙的または明示的に参照するプログラムは、不正な形式です。

明らかに、~A()を明示的に参照しているわけではありません。暗黙的に参照していますか? [class.dtor]/12

デストラクタは暗黙的に呼び出されます

  • プログラムの終了時([basic.start.term])に静的ストレージ期間([basic.stc.static])を持つ構築されたオブジェクトの場合、
  • スレッド終了時にスレッド保存期間([basic.stc.thread])を持つ構築済みオブジェクトの場合、
  • オブジェクトが作成されたブロックが終了するとき([stmt.dcl])、自動ストレージ期間([basic.stc.auto])を持つ構築されたオブジェクトの場合、
  • ライフタイムが終了したときに構築された一時オブジェクト([conv.rval]、[class.temporary])。

または [expr.new]/2

new-expressionがクラス型のオブジェクトの配列を作成する場合、デストラクタが呼び出される可能性があります。

それらのものはありますか?いいえ、自動、静的、またはスレッドの保存期間を持つオブジェクトはありません。構築された一時オブジェクトもありません。また、new-expression作成もありません。配列。ここにはオブジェクトが1つだけあり、それは集約初期化する動的ストレージ期間を持つAです。

~A()を明示的または暗黙的に参照しているわけではないので、そのルールを確認することはできません。したがって、gccのバグ。また、gccはnew A;およびnew A();を受け入れることに注意してください。これらは、このルールに関する限り同じ意味を持ちます。

38
Barry

おそらくgccのバグです。

標準では、新しい式が配列を作成するときにデストラクタが呼び出される可能性があることを指定しています [expr.new]

New-expressionがクラス型のオブジェクトまたはオブジェクトの配列を作成する場合、割り当て関数、割り当て解除関数、およびコンストラクターに対するアクセスとあいまいさの制御が行われます。 new-expressionがクラス型のオブジェクトの配列を作成する場合、デストラクタが呼び出される可能性があります。

重点鉱山

gccは、非配列を作成するときにこの規則も適用しますが、これは暗黙的に標準規則ではありません。 以下のコメントのおかげで、gccはまったく逆のことをしているようです:非配列を作成するとき、デストラクタが呼び出される可能性があるとみなし、配列を作成するとき、デストラクタをチェックしません。

4
Oliv

私の知る限り、この例ではオブジェクトは破壊されていません。また、式がnew A;に変更された場合は、たまたまコンパイルされます

コンパイルしていないサンプルコードはGCCのバグだと思います。 Clangは問題なくコンパイルします


新たに追加されたlanguage-lawyerタグに対する回答。

[class.dtor]の重要な標準ルールは次のとおりです。

デストラクタは暗黙的に呼び出されます

...動的以外のストレージ期間を含む適用されないケース...

...デストラクタは、new-expression(5.3.4)によって割り当てられた構築済みオブジェクトに対してdelete-expression(5.3.5)を使用して暗黙的に呼び出されます。呼び出しのコンテキストはdelete-expressionです。 [注:クラス型の配列には、デストラクタが呼び出されるサブオブジェクトがいくつか含まれています。 — end note]デストラクタは明示的に呼び出すこともできます。デストラクタは、呼び出されるか、5.3.4、12.6.2、および15.1で指定されている場合に呼び出される可能性があります。

5.3.4は[expr.new]のみを指定します

... new-expressionがクラス型のオブジェクトの配列を作成する場合、デストラクタが潜在的に呼び出されます(12.4)。

適用されません。

12.6.2は[class.base.init]で、指定するのは

非委任コンストラクターでは、クラスタイプの潜在的に構築された各サブオブジェクトのデストラクターが潜在的に呼び出されます(12.4)。

当てはまらない

15.1は[except.throw]で、例外オブジェクトの破棄方法を指定しますが、適用されません

結論:セクション5.3.4、12.6.2、および15.1はありません。この場合に適用されるルールが含まれており、デストラクタは呼び出されず、delete-expressionもありません。したがって、デストラクタは潜在的に呼び出されないため、デストラクタを削除するのに適しています。

2
eerorika