[except.ctor]では、標準( N414 )は次のことを保証します。
...デストラクタは、tryブロックが入力されてから構築されたすべての自動オブジェクトに対して呼び出されます。
ただし、次の例では、空の output は、関数foo
の戻り値が作成されていても、破棄されていないことを示しています。 g ++(5.2.1)およびclang ++(3.6.2-1)を使用し、オプション-O0 -fno-elide-constructors -std=c++14
を使用してコンパイルされます。
struct A { ~A() { cout << "~A\n"; } };
struct B { ~B() noexcept(false) { throw 0; } };
A foo() {
B b;
return {};
}
int main() {
try { foo(); }
catch (...) { }
}
これはg ++とclang ++の両方のバグですか、それとも関数の戻り値は自動オブジェクトとは見なされませんか、それともC++言語の抜け穴ですか?
[stmt.return]、[expr.call]、[dcl.fct]のいずれにおいても、関数の戻り値が自動オブジェクトと見なされるかどうかの明確なステートメントを見つけることができませんでした。私が見つけた最も近いヒントは6.3.3p2です。
... returnステートメントには、一時オブジェクトの構築とコピーまたは移動が含まれる場合があります。
および5.2.2p10:
関数呼び出しは、結果タイプが左辺値参照タイプまたは関数タイプへの右辺値参照である場合は左辺値、結果タイプがオブジェクトタイプへの右辺値参照である場合はx値、それ以外の場合はprvalueです。
関数の戻り値は一時的なものと見なされ、戻り値の作成はローカルが破棄される前に順序付けられます。
残念ながら、これは規格では十分に指定されていません。これを説明し、問題を修正するための文言を提供する 未解決の欠陥 があります
[...]型voidのオペランドを持つreturnステートメントは、戻り値の型がcvvoidである関数でのみ使用されます。他のオペランドを含むreturnステートメントは、戻り値の型がcvvoidではない関数でのみ使用されます。 returnステートメントは、オペランドからのコピー初期化(8.5 [dcl.init])によって返されるオブジェクトまたは参照を初期化します。 [...]
返されたエンティティのコピー初期化は、returnステートメントのオペランドによって確立された完全な式の最後で一時変数が破棄される前にシーケンスされます。次に、ローカル変数が破棄される前にシーケンスされます(6.6 [stmt。ジャンプ])returnステートメントを囲むブロックの。
関数の戻り値は一時的なものであるため、投稿の開始時にdestructors are invoked for all automatic objects
引用符でカバーされていません。ただし、[class.temporary]/3
は次のように述べています。
[...]一時オブジェクトは、(字句的に)作成されたポイントを含む完全な式を評価する最後のステップとして破棄されます。 これは、その評価が例外のスローで終了した場合でも当てはまります。 [...]
したがって、これはGCCとClangのバグと見なすことができると思います。
デストラクタから投げないでください;)
これはバグであり、MSVCは実際にそれを正しく理解します。「〜A」を出力します。
コードを変更しましたが、出力からAが破壊されていないことがわかります。
#include<iostream>
using namespace std;
struct A {
~A() { cout << "~A\n"; }
A() { cout << "A()"; }
};
struct B {
~B() noexcept( false ) { cout << "~B\n"; throw(0); }
B() { cout << "B()"; }
};
A foo() {
B b;
return;
}
int main() {
try { foo(); }
catch (...) {}
}
そして、出力は次のとおりです。
B()A()〜B
そうです、それはバグかもしれません。