例外を処理する
例外はハンドラによって処理されます。
目次 |
[編集] ハンドラ
catch ( attr (省略可能) type-specifier-seq declarator ) compound-statement |
(1) | ||||||||
catch ( attr (省略可能) type-specifier-seq abstract-declarator (省略可能) ) compound-statement |
(2) | ||||||||
catch ( ... ) compound-statement |
(3) | ||||||||
| attr | - | (C++11以降) 任意の数の属性。パラメータに適用される。 |
| type-specifier-seq | - | 仮パラメータ宣言の一部で、関数のパラメータリストと同様。 |
| declarator (宣言子) | - | パラメータ宣言の一部で、関数のパラメータリストと同様。 |
| abstract-declarator | - | 名前なしパラメータ宣言の一部で、関数のパラメータリストと同様。 |
| compound-statement | - | 複合ステートメント |
ハンドラ内のパラメータ宣言は、そのハンドラが入力される原因となる例外の型を記述します。
パラメータが以下のいずれかの型として宣言されている場合、プログラムは不正です。
| (C++11以降) |
- (cv修飾子が付いている可能性がある) void 以外の不完全型へのポインタ
- 不完全型への左辺値参照
パラメータが「Tの配列」または関数型Tとして宣言されている場合、型は「Tへのポインタ」に調整されます。
パラメータ型がTのハンドラは、「型Tのハンドラ」と略すことができます。
[編集] 例外のマッチング
各tryブロックは多数のハンドラと関連付けられ、これらのハンドラはハンドラシーケンスを形成します。tryブロックから例外がスローされると、シーケンス内のハンドラが出現順に試され、例外に一致するかどうか確認されます。
以下のいずれかの条件が満たされた場合、ハンドラは型Eの例外オブジェクトと一致します。
- ハンドラが「cv修飾子が付いている可能性がある
T」または「cv修飾子が付いている可能性があるTへの左辺値参照」の型であり、かつ以下のいずれかの条件が満たされる場合:
-
EとTが同じ型である(トップレベルのcv修飾子は無視される)。 -
TがEの曖昧性のない公開基底クラスである。
-
- ハンドラが「cv修飾子が付いている可能性がある
T」またはconst T&の型であり、ここでTがポインタまたはメンバへのポインタ型であり、かつ以下のいずれかの条件が満たされる場合:
-
Eが、以下のいずれかの変換によってTに変換できるポインタまたはメンバへのポインタ型である場合:
- privateまたはprotectedまたは曖昧なクラスへのポインタへの変換を含まない標準ポインタ変換。
-
| (C++17以降) |
- 修飾変換。
|
(C++11以降) |
catch (...)ハンドラは、あらゆる型の例外に一致します。存在する場合、ハンドラシーケンスの最後のハンドラでなければなりません。このハンドラは、nothrow例外保証を提供する関数から、キャッチされない例外が漏れることがないようにするために使用できます。
try { f(); } catch (const std::overflow_error& e) {} // this executes if f() throws std::overflow_error (same type rule) catch (const std::runtime_error& e) {} // this executes if f() throws std::underflow_error (base class rule) catch (const std::exception& e) {} // this executes if f() throws std::logic_error (base class rule) catch (...) {} // this executes if f() throws std::string or int or any other unrelated type
tryブロックのハンドラの中で一致するものが見つからない場合、一致するハンドラの検索は、動的に囲むtryブロック(同じスレッドの)(C++11以降)で続行されます。
一致するハンドラが見つからない場合、std::terminateが呼び出されます。std::terminateのこの呼び出しの前にスタックが巻き戻されるかどうかは実装定義です。
[編集] 例外の処理
例外がスローされると、制御は型が一致する最も近いハンドラに転送されます。「最も近い」とは、tryキーワードに続く複合ステートメントまたはメンバ初期化子リスト(存在する場合)が制御スレッドによって最も最近入力され、まだ終了していないハンドラを意味します。
[編集] ハンドラパラメータの初期化
パラメータリスト(存在する場合)で宣言された、型が「cv修飾子が付いている可能性があるT」または「cv修飾子が付いている可能性があるTへの左辺値参照」のパラメータは、型Eの例外オブジェクトから次のように初期化されます。
TがEの基底クラスである場合、パラメータは例外オブジェクトの対応する基底クラスサブオブジェクトを指定する型Tの左辺値からコピー初期化されます。- それ以外の場合、パラメータは例外オブジェクトを指定する型
Eの左辺値からコピー初期化されます。
パラメータの寿命は、ハンドラ内で初期化された自動記憶期間を持つオブジェクトの破棄後に、ハンドラが終了すると終了します。
パラメータがオブジェクトとして宣言されている場合、そのオブジェクトへの変更は例外オブジェクトに影響しません。
パラメータがオブジェクトへの参照として宣言されている場合、参照されるオブジェクトへの変更は例外オブジェクトへの変更であり、そのオブジェクトが再スローされた場合に影響します。
[編集] ハンドラの起動
ハンドラ(存在する場合)のパラメータの初期化が完了すると、ハンドラはアクティブであるとみなされます。
また、スローによってstd::terminateが入力されると、暗黙のハンドラがアクティブであるとみなされます。
ハンドラが終了すると、ハンドラはアクティブであるとはみなされません。
最も最近アクティブになり、かつまだアクティブなハンドラを持つ例外は、現在処理中の例外と呼ばれます。そのような例外は再スローできます。
[編集] 制御フロー
ハンドラのcompound-statementは制御フロー限定ステートメントです。
void f() { goto label; // error try { goto label; // error } catch (...) { goto label: // OK label: ; } }
[編集] 注釈
スタック巻き戻しは、制御がハンドラに転送されている間に発生します。ハンドラがアクティブになると、スタック巻き戻しはすでに完了しています。
throw 0というthrow式によってスローされた例外は、ポインタ型またはメンバへのポインタ型のハンドラには一致しません。
|
(C++11以降) |
例外オブジェクトは決して配列型や関数型を持つことはできないため、配列型や関数型への参照を持つハンドラは、いかなる例外オブジェクトにも一致しません。
例えば、対応する曖昧性のない公開基底クラスのハンドラの後ろに、最終派生クラスのハンドラを配置するなど、決して実行されないハンドラを作成することも可能です。
try { f(); } catch (const std::exception& e) {} // will be executed if f() throws std::runtime_error catch (const std::runtime_error& e) {} // dead code!
多くの実装は、CWG issue 388の解決策を、非constポインタ型への参照ハンドラにも過度に拡張しています。
int i; try { try { throw static_cast<float*>(nullptr); } catch (void*& pv) { pv = &i; throw; } } catch (const float* pf) { assert(pf == nullptr); // should pass, but fails on MSVC and Clang }
[編集] キーワード
[編集] 例
以下の例は、ハンドラのいくつかの使用例を示しています。
#include <iostream> #include <vector> int main() { try { std::cout << "Throwing an integer exception...\n"; throw 42; } catch (int i) { std::cout << " the integer exception was caught, with value: " << i << '\n'; } try { std::cout << "Creating a vector of size 5... \n"; std::vector<int> v(5); std::cout << "Accessing the 11th element of the vector...\n"; std::cout << v.at(10); // vector::at() throws std::out_of_range } catch (const std::exception& e) // caught by reference to base { std::cout << " a standard exception was caught, with message: '" << e.what() << "'\n"; } }
実行結果の例
Throwing an integer exception... the integer exception was caught, with value: 42 Creating a vector of size 5... Accessing the 11th element of the vector... a standard exception was caught, with message: 'out_of_range'
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 98 | C++98 | switchステートメントは制御をハンドラに転送できる。 | 禁止された |
| CWG 210 | C++98 | throw式がハンドラに対してマッチングされていた。 | 例外オブジェクトが ハンドラに対してマッチングされる。 |
| CWG 388 | C++98 | ポインタまたはメンバへのポインタ型の例外は 異なる型へのconst参照ではマッチングできなかった。 |
変換可能な場合に マッチング可能になった。 |
| CWG 1166 | C++98 | 型が抽象クラス型への参照であるハンドラがマッチングされた場合、 動作は未定義であった。 |
抽象クラス型は ハンドラには許可されない。 |
| CWG 1769 | C++98 | ハンドラの型が例外オブジェクトの型の基底である場合、 変換コンストラクタがハンドラパラメータの初期化に 使用される可能性があった。 |
パラメータは、例外オブジェクトの 対応する基底クラスサブオブジェクトから コピー初期化される。 |
| CWG 2093 | C++98 | オブジェクト型へのポインタの例外オブジェクトは、修飾変換によって オブジェクト型へのポインタのハンドラとマッチングできなかった。 |
許可 |
[編集] 参照
- C++23標準 (ISO/IEC 14882:2024)
- 14.4 例外の処理 [except.handle]
- C++20 standard (ISO/IEC 14882:2020)
- 14.4 例外の処理 [except.handle]
- C++17 standard (ISO/IEC 14882:2017)
- 18.3 例外の処理 [except.handle]
- C++14 standard (ISO/IEC 14882:2014)
- 15.3 例外の処理 [except.handle]
- C++11 standard (ISO/IEC 14882:2011)
- 15.3 例外の処理 [except.handle]
- C++03 標準 (ISO/IEC 14882:2003)
- 15.3 例外の処理 [except.handle]
- C++98 標準 (ISO/IEC 14882:1998)
- 15.3 例外の処理 [except.handle]