Dynamic exception specification (until C++17)
関数が直接的または間接的にスローする可能性のある例外をリストします。
目次 |
構文
throw(
type-id-list
(optional)
)
|
(1) |
(C++11で非推奨)
(C++17で削除) |
|||||||
| type-id-list | - | カンマ区切りの type-id のリスト 、パック展開を表すtype-idの後には省略記号(...)が続く (C++11以降) |
明示的な動的例外指定は、宣言または定義のトップレベル型である関数型、関数へのポインタ型、関数への参照型、またはメンバ関数へのポインタ型の関数宣言子、あるいは関数宣言子内のパラメータまたは戻り値型として現れるそのような型に対してのみ現れなければならない。
void f() throw(int); // OK: 関数宣言 void (*pf)() throw (int); // OK: 関数ポインタ宣言 void g(void pfa() throw(int)); // OK: 関数ポインタパラメータ宣言 typedef int (*pf)() throw(int); // エラー: typedef宣言
説明
関数がその動的例外指定にリストされた型
T
で宣言されている場合、その関数はその型またはそこから派生した型の例外をスローする可能性があります。
不完全型
、cv修飾された
void*
以外の不完全型へのポインタまたは参照
、および右辺値参照型
(C++11以降)
は例外指定で許可されません。配列型および関数型が使用された場合、対応するポインタ型に調整され、トップレベルのcv修飾子も削除されます。
パラメータパック
は許可されます
(C++11以降)
。
調整後の型集合が空である動的例外仕様 (すべてのパックが展開された後) (C++11以降) は非スローです。非スロー動的例外仕様を持つ関数は、いかなる例外も許可しません。
動的例外仕様は関数の型の一部とは見なされません。
関数がその例外仕様に列挙されていない型の例外をスローした場合、 std::unexpected が呼び出されます。デフォルトの関数は std::terminate を呼び出しますが、ユーザー提供の関数( std::set_unexpected 経由)で置き換えることが可能です。この関数は std::terminate を呼び出すか、例外をスローすることができます。 std::unexpected からスローされた例外が例外仕様で許可されている場合、スタックの巻き戻しは通常通り継続します。許可されていないが、例外仕様で std::bad_exception が許可されている場合、 std::bad_exception がスローされます。それ以外の場合、 std::terminate が呼び出されます。
インスタンス化
関数テンプレートの特殊化における動的例外指定は、関数宣言と共にインスタンス化されません。それは 必要とされる場合 にのみ(以下で定義される通り)インスタンス化されます。
暗黙的に宣言された特殊メンバ関数の動的例外指定も、必要なときにのみ評価されます(特に、派生クラスのメンバ関数の暗黙的な宣言では、基底メンバ関数の例外指定がインスタンス化される必要はありません)。
関数テンプレートの特殊化の動的例外仕様が 必要とされる が、まだインスタンス化されていない場合、依存名のルックアップが行われ、 expression 内で使用されるテンプレートは、特殊化の宣言と同様にインスタンス化されます。
関数の動的例外仕様は、以下の文脈において 必要とされる と見なされます:
- 式内で、オーバーロード解決によって関数が選択される場合
- 関数が ODR-use される場合
- 関数はODR-useされるはずだが、未評価オペランド内に現れる場合
template<class T> T f() throw(std::array<char, sizeof(T)>); int main() { decltype(f<void>()) *p; // fは評価されないが、例外仕様が必要 // 例外仕様のインスタンス化がsizeof(void)を計算するためエラー }
- この仕様は、別の関数宣言(例えば仮想関数のオーバーライドや関数テンプレートの明示的特殊化)と比較するために必要です
- 関数定義内で
- デフォルト化された特殊メンバー関数が自身の例外仕様を決定するためにこれをチェックする必要があるため、仕様が必要です(これはデフォルト化された特殊メンバー関数の仕様自体が必要とされる場合にのみ発生します)。
潜在的な例外
各関数
f
、関数へのポインタ
pf
、およびメンバ関数へのポインタ
pmf
は、
潜在的な例外のセット
を持ちます。これはスローされる可能性のある型で構成されます。すべての型のセットは、あらゆる例外がスローされる可能性があることを示します。このセットは以下のように定義されます:
f
、
pf
、または
pmf
の宣言が動的例外仕様を使用している場合
すべての例外を許可しないもの
(C++11まで)
、その集合はその仕様に列挙された型で構成される。
| (C++11以降) |
注意: 暗黙的に宣言される特殊メンバ関数(コンストラクタ、代入演算子、デストラクタ) および継承コンストラクタ (C++11以降) について、潜在的な例外の集合は、それらが呼び出すすべての要素(非volatile非staticデータメンバ、直接基底クラス、および適切な場合には仮想基底クラスのコンストラクタ/代入演算子/デストラクタ)の潜在的な例外の集合の組み合わせです(常に通り、デフォルト引数式を含む)。
各式々の式
e
は
潜在的な例外の集合
を持ちます。この集合は、
e
が
コア定数式
である場合は空集合となります。それ以外の場合、この集合は
e
のすべての直接の部分式(
デフォルト引数式
を含む)の潜在的な例外の集合の和集合と、
e
の形式に依存する別の集合を組み合わせたものとなります:
e
が関数呼び出し式である場合、
g
を呼び出される関数、関数ポインタ、またはメンバ関数へのポインタとすると、
-
-
gの宣言が動的例外仕様を使用している場合、gの潜在的な例外の集合がセットに追加される;
-
|
(C++11以降) |
-
- それ以外の場合、セットは全ての型の集合となる。
e
が暗黙的に関数を呼び出す場合(演算子式であり演算子がオーバーロードされている、
new-expression
でありアロケーション関数がオーバーロードされている、または完全式であり一時オブジェクトのデストラクタが呼び出される場合)、その集合はその関数の集合となる。
e
が
throw-expression
である場合、セットはそのオペランドによって初期化される例外、または(オペランドを持たない)再スローのthrow-expressionに対する全ての型のセットとなります。
|
6)
もし
e
が非定数の配列サイズを持つ
new-expression
であり、選択されたアロケーション関数が空でない潜在的な例外の集合を持つ場合、その集合は
std::bad_array_new_length
を含む。
|
(C++11以降) |
void f() throw(int); // f()の例外指定セットは"int" void g(); // g()の例外指定セットは全ての型の集合 struct A { A(); }; // "new A"の例外指定セットは全ての型の集合 struct B { B() noexcept; }; // "B()"の例外指定セットは空 struct D() { D() throw (double); }; // new Dの例外指定セットは全ての型の集合
暗黙的に宣言されるすべてのメンバ関数 および継承コンストラクタ (C++11以降) は、次のように選択される例外仕様を持ちます:
- 潜在的な例外の集合がすべての型である場合、暗黙の例外仕様は すべての例外を許可する(例外仕様は存在すると見なされるが、コードでは表現できず、例外仕様がない場合と同様に動作する) (C++11まで) noexcept ( false ) である (C++11以降) 。
- それ以外の場合、潜在的な例外の集合が空でない場合、暗黙の例外仕様はその集合のすべての型を列挙する。
- それ以外の場合、暗黙の例外仕様は throw ( ) (C++11まで) noexcept ( true ) (C++11以降) である。
struct A { A(int = (A(5), 0)) noexcept; A(const A&) throw(); A(A&&) throw(); ~A() throw(X); }; struct B { B() throw(); B(const B&) = default; // 例外指定は "noexcept(true)" B(B&&, int = (throw Y(), 0)) noexcept; ~B() throw(Y); }; int n = 7; struct D : public A, public B { // std::bad_array_new_length 型のハンドラに一致する型の例外をスローする可能性があるが、 // バッドアロケーション例外はスローしない (void*) new (std::nothrow) int[n]; // D は以下の暗黙的に宣言されるメンバを持つ可能性がある: // D::D() throw(X, std::bad_array_new_length); // D::D(const D&) noexcept(true); // D::D(D&&) throw(Y); // D::~D() throw(X, Y); };
注記
Clangは、動的例外仕様のインスタンス化ルールがC++11において CWG1330 によって変更されたと見なしています。詳細は LLVM #56349 を参照してください。
キーワード
例
注意:警告を避けるためにはC++98モードでコンパイルすることを推奨します。C++17以降のリビジョンとは互換性がありません。
#include <cstdlib> #include <exception> #include <iostream> class X {}; class Y {}; class Z : public X {}; class W {}; void f() throw(X, Y) { bool n = false; if (n) throw X(); // OK, would call std::terminate() if (n) throw Z(); // also OK throw W(); // will call std::unexpected() } void handler() { std::cerr << "That was unexpected!\n"; // flush needed std::abort(); } int main() { std::set_unexpected(handler); f(); }
出力:
That was unexpected!
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 25 | C++98 |
異なる例外指定を持つメンバへのポインタ間の
代入と初期化の動作は未規定であった |
関数ポインタと参照に対する
制限を適用する |
| CWG 973 | C++98 |
例外指定には関数型を含むことができるが、
対応する関数ポインタ変換は規定されていなかった |
規定された |
| CWG 1330 | C++98 | 例外指定は積極的にインスタンス化される可能性があった | 必要な場合にのみインスタンス化される |
| CWG 1267 | C++11 | 例外指定で右辺値参照型が許可されていた | 許可されない |
| CWG 1351 |
C++98
C++11 |
デフォルト引数(C++98)とデフォルトメンバ初期化子
(C++11)は暗黙の例外指定で無視されていた |
考慮されるようにした |
| CWG 1777 | C++11 |
throw
(
T...
)
は、たとえ
T
が空のパックでも
非スロー指定ではなかった |
パックが空の場合
非スローとなる |
| CWG 2191 | C++98 |
typeid
式の潜在的な例外の集合には、
スローされない場合でも
bad_typeid
が含まれる可能性があった
|
スローされる可能性がある場合にのみ
bad_typeid
を含む
|
関連項目
noexcept
指定子
(C++11)
|
関数が例外をスローする可能性があるかどうかを指定する |