cppreferenceでわかるよう に、従来の「スロー」宣言リストは C++11 では非推奨になりました。このメカニズムを残す理由は何ですか?また、どの例外が私の関数をスローするかをどのように指定する必要がありますか?
3 に答える
詳細な理由については、http ://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3051.htmlを参照してください。
上記の国家機関のコメントで述べられているように、例外仕様は実際には有用であることが証明されていません。C ++の例外仕様に関する問題については多くの議論がありますが(たとえば、[Sutter02]、[Boost03]を参照)、主な問題は次のとおりです。
- 実行時チェック:C ++例外仕様は、コンパイル時ではなく実行時にチェックされるため、すべての例外が処理されたことをプログラマーが保証することはできません。実行時の障害モード(std :: unexpected()の呼び出し)は、回復には役立ちません。
- 実行時のオーバーヘッド:実行時のチェックでは、コンパイラーが最適化を妨げる追加のコードを生成する必要があります。
- ジェネリックコードでは使用できません:ジェネリックコード内では、テンプレート引数の操作からスローされる可能性のある例外のタイプを知ることは一般に不可能であるため、正確な例外仕様を記述できません。
実際には、2つの形式の例外スロー保証のみが役立ちます。操作が例外(任意の例外)をスローする場合と、操作が例外をスローしない場合です。前者は例外仕様を完全に省略して表現され、後者はthrow()として表現できますが、パフォーマンス上の理由から、まれにしか表現されません。
[N3050]は、新しい種類の例外仕様noexceptを導入しました。これは、関数が例外をスローしないことを指定します。throw()とは異なり、noexceptは、例外がスローされるかどうかをチェックするためのコードをコンパイラーに導入する必要はありません。むしろ、noexceptとして指定された関数が例外を介して終了した場合、結果はstd :: terminate()の呼び出しになります。
noexceptの導入により、プログラマーは、追加のオーバーヘッドなしで、実際に役立つ2種類の例外保証を表現できるようになりました。したがって、このペーパーでは、「動的な」例外仕様、つまり、throw(type-id-listopt)として記述された仕様を非推奨にすることを提案します。
Peter の答えは、実装者とユーザーの例外仕様の実際の問題には当てはまりません。
- 実装者が定義された例外のみをスローするという保証を維持できなかった場合、例外仕様はプログラムを終了させます (より正確には終了ハンドラーを呼び出します)。
- したがって、例外指定を使用してメソッドを呼び出すことにより、ライブラリ ユーザーとして、独自のコードが完全な失敗/終了を起こしやすくなります。ライブラリ関数がメモリ不足 (std::bad_alloc) で実行された場合、キャッチする機会はありませんが、代わりに終了します。
- したがって、最も可能性の高い障害オプションを伝え、ユーザーとしてそれらを処理するように依頼するという当初の目標は達成されませんでした。
- 反対側の実装者として、例外仕様を持たない他のメソッドを実際に呼び出すことはできません。これにより、呼び出し元が終了する可能性があるためです。ひどい場所です。
結論として、C++ は Java が行ったようにすべきだったということです。
- 例外指定を使用してメソッドを呼び出し、自分で例外指定を持っている場合は、例外をキャッチするか、独自の例外指定で指定する必要があります。
- コンパイラはこれを強制し、その他のランタイム効果はありません。
Noexcept (C++11 以降) も同じ概念上の誤りを被ります。これは、仕様にも準拠していない場合 (つまり、宣言されていないメソッドがスローされた場合) にもランタイム終了が発生するためです。これによりnoexcept
、深刻なケース以外には使用できなくなります (ムーブ コンストラクターが思い浮かびます)。
libc++ は、関数から伝播する例外がその例外仕様に違反しているかどうかをチェックし、std::unexpected
. これはほとんど役に立たず、関数がスローする例外を単に文書化するよりも悪いことです。