2016-01-08 6 views
56

は標準(N4140は)ことを保証...関数の戻り値は自動オブジェクトなので、破壊されていることが保証されていますか? 【except.ctor]で

...デストラクタは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 (...) { } 
} 

これは++グラム++にと打ち鳴らすの両方のバグで、または関数の戻り値ではないと考え 自動オブジェクトである、またはそれは、C++言語のループ穴のですか?

[stmt.return]、[expr.call]または[dcl.fct]のいずれも、関数戻り値が自動 オブジェクトと見なされるかどうかを明確に示す が見つかりました。

... return文が 建設に関与してコピーするか、一時的なオブジェクトの移動ができます...

および5.2.2 P10:私が見つけた最も近いヒントは6.3.3 p2としています:

関数呼び出しは、結果タイプは左辺値 参照型またはタイプを機能させる右辺値参照、 結果型は型のオブジェクトへの右辺値参照がある場合はxValueである場合左辺値であり、そしてprvalueさもなければ。

答えて

45

関数の戻り値は一時的なものとみなされ、戻り値の構成は地方の破壊の前に順序付けされます。

残念ながら、これは標準では規定されていません。このことを説明し、問題に

を修正するために、いくつかの文言を提供していますopen defectはvoid型のオペランドとreturn文が唯一のその戻り値の型のCV無効である機能で使用されなければならない[...]があります。他のオペランドとのreturn文は、戻り値の型がcv voidでない関数でのみ使用されます。 return文は、オペランドからコピー初期化(8.5 [dcl.init])によって返されるオブジェクトまたは参照を初期化します。 [...]

返されたエンティティのコピー初期化は、returnステートメントのオペランドによって確立された完全式の終わりに一時オブジェクトが破棄される前に順序付けされます。これはローカル変数の破棄前にシーケンスされます(6.6 [stmt.jump])を返します。

関数の戻り値は一時的なものなので、投稿開始時のdestructors are invoked for all automatic objects見積もりには含まれません。しかし、[class.temporary]/3は言う:

[...]一時的なオブジェクトは(字句)が作成されたポイントが含まれていることを完全な式を評価する際の最後のステップとして破棄されます。 この評価は、例外をスローすることで終了しても当てはまります。 [...]

だから、これはGCCとClangのバグと考えることができると思います。

デストラクタから捨ててはいけない;)

+6

私は、gccとclangの両方がすでに何年も前からこのバグを提起していることを知ったので、すぐに修正するとは思っていません:[gcc](https://gcc.gnu.org/bugzilla/show_bug.cgi ?id = 33799)、[clang](https://llvm.org/bugs/show_bug.cgi?id=12286)。 –

+0

この場合、コンパイラはオブジェクトAの構築を省略できますか?私はRVOに似たいくつかのルールを意味する。 – Mikhail

+0

@Mikhail私はそう信じていない。 RVOは構造を完全に取り除くことはできません。中間構造を取り除くことができます。 – TartanLlama

7

これはバグであり、かつてはMSVCが実際にそれを正しく取得します。「〜A」が表示されます。

+0

いくつかの時間が打ち鳴らすとgccがコードを拒否したが、MSVCは、定義された動作を使用してコードを受け付け参照してください。尋ねられれば私は一度も起こった、私は例を提供することができます。 –

+6

関数の戻り値が自動オブジェクトと見なされているか、それ以外の場合gccとclangのバグであることが証明されていることを証明する標準への参照を追加してください。 –

+0

@FlorianKaufmann私は[この例では](http://coliru.stacked-crooked.com/a/ee1a5d76230ec21c)は、スコープ外に出てもデストラクタが呼び出されない「A」オブジェクトがあることを示していると思います作成された後。 – Antonio

7

私はあなたのコードを修正しました。私は出力から、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

そうです、それはバグである可能性があります。

関連する問題