例外を送出する
例外はthrow 式からスローできます。以下のコンテキストも例外をスローする可能性があります。
目次 |
[編集] 例外オブジェクト
例外をスローすると、動的ストレージ期間を持つオブジェクトが初期化されます。これを例外オブジェクトと呼びます。
例外オブジェクトの型が以下のいずれかの型になる場合、プログラムは不適格です。
[編集] 例外オブジェクトの構築と破棄
例外オブジェクトの型をTとする。
- objを型const Tの左辺値とすると、objからの型
Tのオブジェクトのコピー初期化は適格でなければなりません。 Tがクラス型の場合、
例外オブジェクトのメモリは未指定の方法で割り当てられます。唯一の保証は、ストレージがグローバルな割り当て関数によって割り当てられることは決してないということです。
ハンドラが再スローによって終了する場合、制御は同じ例外オブジェクトの別のハンドラに渡されます。この場合、例外オブジェクトは破棄されません。
|
例外の最後に残っているアクティブなハンドラが再スロー以外の方法で終了すると、例外オブジェクトは破棄され、実装は未指定の方法で一時オブジェクトのメモリを解放する場合があります。 破棄は、ハンドラの「パラメータリスト」で宣言されたオブジェクトの破棄の直後に発生します。 |
(C++11まで) |
|
例外オブジェクトの潜在的な破棄ポイントは次のとおりです。
例外オブジェクトのすべての潜在的な破棄ポイントの中で、例外オブジェクトが破棄される未指定の最後のポイントがあります。他のすべてのポイントは、その最後のポイントよりも前に発生します。その後、実装は未指定の方法で例外オブジェクトのメモリを解放する場合があります。 |
(C++11以降) |
[編集] throw 式
throw expression |
(1) | ||||||||
throw
|
(2) | ||||||||
| 式 | - | 例外オブジェクトを構築するために使用される式 |
新しい例外がスローされるとき、その例外オブジェクトは次のように決定されます。
- expressionに対して、配列からポインタへのおよび関数からポインタへの標準変換が実行されます。
- 変換結果をexとします。
- 例外オブジェクトの型は、exの型からトップレベルのcv修飾子をすべて削除することによって決定されます。
- 例外オブジェクトはexからコピー初期化されます。
プログラムが、現在処理中の例外がないときに例外を再スローしようとすると、std::terminateが呼び出されます。それ以外の場合、既存の例外オブジェクト(新しい例外オブジェクトは作成されません)で例外が再アクティブ化され、例外は捕捉されたとは見なされなくなります。
try { // throwing a new exception 123 throw 123; } catch (...) // catch all exceptions { // respond (partially) to exception 123 throw; // pass the exception to some other handler }
[編集] スタックアンワインド
例外オブジェクトが構築されると、制御フローはtryブロックの開始に到達するまで(コールスタックを)逆方向に(上に)進みます。この時点で、関連するすべてのハンドラのパラメータが、例外オブジェクトの型と比較され、出現順に一致するものが検索されます。一致するものが見つからない場合、制御フローは次のtryブロックまでスタックのアンワインドを続行します。一致するものが見つかった場合、制御フローは一致するハンドラにジャンプします。
制御フローがコールスタックを上に移動すると、対応するtryブロックに入ってから構築されたがまだ破棄されていない、自動ストレージ期間を持つすべてのオブジェクトのデストラクタが、コンストラクタの完了とは逆の順序で呼び出されます。ローカル変数またはreturn文で使用される一時オブジェクトのデストラクタから例外がスローされた場合、関数から返されたオブジェクトのデストラクタも呼び出されます。
オブジェクトのコンストラクタまたは(稀に)デストラクタから例外がスローされた場合(オブジェクトのストレージ期間に関わらず)、完全に構築された非静的非バリアントメンバと基底クラスのすべてのデストラクタが、コンストラクタの完了とは逆の順序で呼び出されます。共用体ライクなクラスのバリアントメンバは、コンストラクタからのアンワインドの場合にのみ破棄され、初期化と破棄の間にアクティブなメンバが変更された場合、動作は未定義です。
|
非委譲コンストラクタが正常に完了した後、委譲コンストラクタが例外で終了した場合、このオブジェクトのデストラクタが呼び出されます。 |
(C++11以降) |
new式によって呼び出されたコンストラクタから例外がスローされた場合、利用可能であれば、一致するデアロケーション関数が呼び出されます。
このプロセスをスタックアンワインドと呼びます。
例外オブジェクトの初期化後、例外ハンドラの開始前に、スタックアンワインド機構によって直接呼び出される関数が例外で終了した場合、std::terminateが呼び出されます。そのような関数には、スコープから出た自動ストレージ期間を持つオブジェクトのデストラクタ、および値渡し引数を初期化するために(省略されない限り)呼び出される例外オブジェクトのコピーコンストラクタが含まれます。
std::threadの初期関数、main関数、および静的またはスレッドローカルオブジェクトのコンストラクタまたはデストラクタから脱出する例外を含め、例外がスローされて捕捉されない場合、std::terminateが呼び出されます。捕捉されない例外に対してスタックアンワインドが行われるかどうかは、実装定義です。
[編集] 注釈
例外を再スローする場合、例外オブジェクトが継承を使用する(一般的な)ケースでオブジェクトスライスを避けるために、2番目の形式を使用する必要があります。
try { std::string("abc").substr(10); // throws std::out_of_range } catch (const std::exception& e) { std::cout << e.what() << '\n'; // throw e; // copy-initializes a new exception object of type std::exception throw; // rethrows the exception object of type std::out_of_range }
throw式は、型voidのprvalue式として分類されます。他の式と同様に、別の式の副式となることがあり、最も一般的には条件演算子で使用されます。
double f(double d) { return d > 1e7 ? throw std::overflow_error("too big") : d; } int main() { try { std::cout << f(1e10) << '\n'; } catch (const std::overflow_error& e) { std::cout << e.what() << '\n'; } }
[編集] キーワード
[編集] 例
#include <iostream> #include <stdexcept> struct A { int n; A(int n = 0): n(n) { std::cout << "A(" << n << ") constructed successfully\n"; } ~A() { std::cout << "A(" << n << ") destroyed\n"; } }; int foo() { throw std::runtime_error("error"); } struct B { A a1, a2, a3; B() try : a1(1), a2(foo()), a3(3) { std::cout << "B constructed successfully\n"; } catch(...) { std::cout << "B::B() exiting with exception\n"; } ~B() { std::cout << "B destroyed\n"; } }; struct C : A, B { C() try { std::cout << "C::C() completed successfully\n"; } catch(...) { std::cout << "C::C() exiting with exception\n"; } ~C() { std::cout << "C destroyed\n"; } }; int main () try { // creates the A base subobject // creates the a1 member of B // fails to create the a2 member of B // unwinding destroys the a1 member of B // unwinding destroys the A base subobject C c; } catch (const std::exception& e) { std::cout << "main() failed to create C with: " << e.what(); }
出力
A(0) constructed successfully A(1) constructed successfully A(1) destroyed B::B() exiting with exception A(0) destroyed C::C() exiting with exception main() failed to create C with: error
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 499 | C++98 | バウンドが不明な配列はスローできませんでした。 その型は不完全ですが、例外オブジェクトは 減衰したポインタから問題なく |
作成できます 型補完要件を 代わりに例外オブジェクトに |
| 適用する | C++98 | std::terminateは、例外がスローされた場合に呼び出されませんでした。 ローカルの非自動オブジェクトのデストラクタから |
std::terminateを呼び出す この場合に |
| CWG 1863 | C++11 | 移動のみの例外オブジェクトがスローされたときに、コピーコンストラクタは必須ではありませんでしたが、後でコピーが許可されました。 コピーコンストラクタが必須 |
コピーコンストラクタが必須 |
| CWG 1866 | C++98 | コンストラクタからのスタックアンワインド時にバリアントメンバがリークしました。 | バリアントメンバが破棄される |
| CWG 2176 | C++98 | ローカル変数のデストラクタからのthrowは 戻り値のデストラクタをスキップする可能性があります |
関数戻り値が アンワインドに追加される |
| CWG 2699 | C++98 | throw "EX"は実際にはconst char*ではなくchar*をスローします。 | 修正済み |
| CWG 2711 | C++98 | 例外オブジェクトのコピー初期化の ソースが指定されていませんでした |
expressionから コピー初期化される |
| CWG 2775 | C++98 | 例外オブジェクトのコピー初期化要件が不明確でした。 | 明確化された |
| CWG 2854 | C++98 | 例外オブジェクトのストレージ期間が不明確でした。 | 明確化された |
| P1825R0 | C++11 | throwでのパラメータからの暗黙的な移動は禁止されていました。 |
許可 |
[編集] 参照
- C++23標準 (ISO/IEC 14882:2024)
- 7.6.18 Throwing an exception [expr.throw]
- 14.2 Throwing an exception [except.throw]
- C++20 standard (ISO/IEC 14882:2020)
- 7.6.18 Throwing an exception [expr.throw]
- 14.2 Throwing an exception [except.throw]
- C++17 standard (ISO/IEC 14882:2017)
- 8.17 Throwing an exception [expr.throw]
- 18.1 Throwing an exception [except.throw]
- C++14 standard (ISO/IEC 14882:2014)
- 15.1 Throwing an exception [except.throw]
- C++11 standard (ISO/IEC 14882:2011)
- 15.1 Throwing an exception [except.throw]
- C++03 標準 (ISO/IEC 14882:2003)
- 15.1 Throwing an exception [except.throw]
- C++98 標準 (ISO/IEC 14882:1998)
- 15.1 Throwing an exception [except.throw]
[編集] 関連項目
| (C++17まで) |