このページはC++17に採用された言語機能の変更を解説しています。
のちのC++規格でさらに変更される場合があるため関連項目を参照してください。
概要
C++11でnoexcept
機能が入ったことにより、従来のthrow
キーワードを使用した「例外仕様 (Exception Specification)」は「動的例外仕様 (Dynamic Exception Specification)」という名前に変更され、非推奨となっていた。
// 動的例外仕様を使用したコード。
// C++11から非推奨、C++17ではコンパイルエラーとなる。
void f() throw(std::runtime_error);
C++17ではこの動的例外仕様が削除される。以降は、noexcept例外仕様を使用すること。
// C++11で導入されたnoexcept例外仕様を使用したコード。
// noexceptおよびnoexcept(true)は、「この関数からは例外を送出しない」という宣言。
// noexcept(false)およびnoexceptを付けないことは「この関数からは例外を送出する可能性がある」という宣言。
void f() noexcept;
void f() noexcept(true);
void f() noexcept(false);
void f();
動的例外仕様では指定された例外以外が送出された場合、std::unexpected()
関数が呼び出されるが、noexcept例外仕様では即座にstd::terminate()
関数が呼び出され、プログラムが異常終了する。
void f() throw();
という「この関数は例外を送出しない」指定については、C++17からnoexcept
を指定したものと同じ意味論に変更され、非推奨の機能として維持される。非推奨の機能ではあるため、削除までの猶予期間の間にnoexcept
例外仕様への移行が必要となる。
また、動的例外仕様に関連したライブラリ機能として、非推奨となっていた以下の機能も削除される:
関連機能としてstd::bad_exception
例外型もあるが、この機能はstd::current_exception()
関数から参照されるものとして残る。
この機能を削除するに至った背景・経緯
動的例外仕様はJavaの「検査例外 (checked exception)」機能と近い機能ではあるが、これらには共通の問題がある。それは、オブジェクト指向プログラミングやジェネリックプログラミングとの相性が悪いということである。
オブジェクト指向プログラミングとの相性が悪いというのは、動的例外仕様を使用してオブジェクト指向プログラミングにおける「開放閉鎖原則 (open/closed principle)」を適用するのが難しいことにある。開放閉鎖原則は「拡張に対して開いていて、修正に対して閉じていなければならない」というものである。仮想関数に対して動的例外仕様を使用して送出される例外型を制限した場合、派生クラスでオーバーライドした仮想関数は、起こりうるエラーの種類に過度な制限を受けてしまう。これはたとえばリソースを読み込むクラス・関数を設計した場合、基底クラスでは「読み込みエラー」くらいしか起こりうるエラーの種類を規定できないが、派生クラスでは「ファイルからの読み込みに失敗」や「ネットワークの接続に失敗」といった起こりうるエラーの種類が増える、といった状況に対応できない。
ジェネリックプログラミングとの相性が悪いというのは、パラメータ化された型によって起こりうるエラーに差異が出るため、起こりうるエラーを列挙することが困難であるという問題である。
C++固有の問題としては、以下のような問題があった:
- 例外型の検査が実行時に行われるために、プログラマが動作を保証できず、回復にも役に立たない。また、実行時の検査であるために、コンパイラは最適化を妨げるコードを生成せざるをえなかった
これらの問題があるため、C++とJavaの後発であるC#では、動的例外仕様や検査例外に類する機能は提供されなかった。
- The Trouble with Checked Exceptions (検査例外でのトラブル)
- C#設計者であるAnders Hejlsbergへのインタビュー
C++でもJavaを含むこれらの問題は認識されており、動的例外仕様の代わりとなるnoexcept例外仕様が新設された。代わりの機能ができたことで動的例外仕様は非推奨となり、noexcept例外仕様への移行する猶予期間が設けられた後、C++17でこの機能が削除されることとなった。