web-dev-qa-db-ja.com

デストラクタ例外の戻り値の破壊

私は次のコードを持っています:

#include <stdexcept>
#include <iostream>

struct ok {
    int _n;
    ok(int n) : _n(n) { std::cerr << "OK" << n << " born" << std::endl; }
    ~ok() {  std::cerr << "OK" << _n << " gone" << std::endl; }
};

struct problematic {
    ~problematic() noexcept(false) { throw std::logic_error("d-tor exception"); }
};

ok boo() {
    ok ok1{1};
    problematic p;
    ok ok2{2};
    return ok{3}; // Only constructor is called...
}

int main(int argc, char **argv) {
    try {boo();} catch(...) {}
}

Ok {3}のデストラクタは呼び出されないことがわかります。出力は次のとおりです。

 OK1 born
 OK2 born
 OK3 born
 OK2 gone
 OK1 gone

C++ 14で予期される動作ですか?

編集:

Gcc 6.3でコンパイルする

38
Evgeny

標準に従って、この動作は間違っており、これは既に質問のコメントセクションで言及されています。これは 例外処理 のセクションで述べられています。

defect reports at open-std.org により、彼らは実装(GCCとClang)が2015-09-28には早くもこれについて間違っていることを認識しています。しかし、提案された解決策は2016年2月にのみであり、コンパイラ(GCCおよびClang)にはまだこの修正が含まれていません。

提案された解像度(2016年2月):

18.2 [except.ctor]パラグラフ2を次のように変更します。
デストラクタは、構築されたクラスタイプの自動オブジェクトごとに呼び出されますが、tryブロックに入ったため、まだ破棄されていません。 returnステートメント(9.6.3 [stmt.return])の一時変数またはローカル変数の破棄中に例外がスローされた場合、返されたオブジェクト(存在する場合)のデストラクタも呼び出されます。オブジェクトは、構築の完了と逆の順序で破棄されます。 [例:

  struct A { };

  struct Y { ~Y() noexcept(false) { throw 0; } };

  A f() {
    try {
      A a;
      Y y;
      A b;
      return {};   // #1
    } catch (...) {
    }
    return {};     // #2
  }

#1で、返されたタイプAのオブジェクトが構築されます。次に、ローカル変数bが破棄されます(9.6 [stmt.jump])。次に、ローカル変数yが破棄され、スタックが巻き戻されて、返されたオブジェクトが破棄され、続いてローカル変数aが破棄されます。最後に、返されたオブジェクトが#2で再び構築されます。 —終了例]

[〜#〜] gcc [〜#〜]Clang の両方で、この問題に対してバグが報告されています。

GCCバグレポートのコメントは、それが明らかにバグであることを示しています。

ジョナサンウェイクリー コメント:

2013年になったので、デストラクタがスローできる場合、値を返すことは賢明なことではありません。

そして別のユーザー:

はい、私は気づきました、そして、Clangは彼らに対して長年苦しんでいるバグも報告しました。それにもかかわらず、動作は間違っています。

23
P.W