動的例外仕様 (C++17まで)
関数が直接的または間接的にスローする可能性のある例外をリストします。
目次 |
[編集] 構文
throw(type-id-list (オプション)) |
(1) | (C++11で非推奨) (C++17で削除) | |||||||
| type-id-list | - | カンマ区切りのtype-idのリスト、パック展開を表すtype-idの後には省略記号(...)が続きます(C++11以降) |
明示的な動的例外仕様は、宣言または定義の最上位型である関数型、関数型へのポインタ、関数型への参照、またはメンバ関数型へのポインタの関数宣言子、あるいは関数宣言子におけるパラメータ型または戻り値型として現れるそのような型にのみ現れるものとします。
void f() throw(int); // OK: function declaration void (*pf)() throw (int); // OK: pointer to function declaration void g(void pfa() throw(int)); // OK: pointer to function parameter declaration typedef int (*pf)() throw(int); // Error: typedef declaration
[編集] 説明
関数がその動的例外仕様にリストされている型Tで宣言されている場合、その関数はその型またはその派生型で例外をスローする可能性があります。
不完全型、cv void*以外の不完全型へのポインタまたは参照、および右辺値参照型(C++11以降)は、例外仕様では許可されません。配列型と関数型は、使用される場合、対応するポインタ型に調整され、最上位のcv修飾子も削除されます。パラメータパックは許可されます(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 unevaluated, but exception specification is needed // error because instantiation of the exception specification // calculates sizeof(void) }
- 例外仕様が他の関数宣言と比較するために必要な場合(例:仮想関数のオーバーライダー、または関数テンプレートの明示的な特殊化の場合)
- 関数定義内
- 例外仕様が必要なのは、デフォルト化された特殊メンバ関数が、自身の例外仕様を決定するためにそれをチェックする必要がある場合(これは、デフォルト化された特殊メンバ関数の例外仕様自体が必要とされる場合にのみ発生します)。
[編集] 潜在的な例外
各関数 f、関数ポインタ pf、およびメンバ関数ポインタ pmf には、スローされる可能性のある型の *潜在的な例外のセット* があります。すべての型のセットは、あらゆる例外がスローされる可能性を示します。このセットは次のように定義されます。
f、pf、または pmf の宣言が動的例外仕様を使用しており、すべての例外を許可しない場合(C++11まで)、そのセットはその仕様にリストされている型で構成されます。| (C++11以降) |
注:暗黙宣言された特殊メンバ関数(コンストラクタ、代入演算子、デストラクタ)および継承コンストラクタ(C++11以降)の場合、潜在的な例外のセットは、それらが呼び出すすべてのもの(非バリアント非静的データメンバのコンストラクタ/代入演算子/デストラクタ、直接基底、および適切な場合は仮想基底(常にデフォルト引数式を含む))の潜在的な例外のセットの組み合わせです。
各式 e には、*潜在的な例外のセット* があります。e が コア定数式 の場合、そのセットは空です。それ以外の場合、それは e のすべての直接部分式の潜在的な例外のセット(デフォルト引数式 を含む)の和集合であり、次のいずれかによって決定される別のセットと組み合わされます。
e が関数呼び出し式の場合、g を呼び出される関数、関数ポインタ、またはメンバ関数ポインタとすると、gの宣言が動的例外仕様を使用している場合、gの潜在的な例外のセットがセットに追加されます。
|
(C++11以降) |
- それ以外の場合、セットはすべての型のセットです。
e が暗黙的に関数を呼び出す場合(演算子式で演算子がオーバーロードされている、new式でアロケーション関数がオーバーロードされている、または完全な式で一時オブジェクトのデストラクタが呼び出される場合)、そのセットはその関数のセットになります。|
6) e が非定数配列サイズを持つ new式であり、選択されたアロケーション関数が空でない潜在的例外のセットを持っている場合、そのセットは std::bad_array_new_length で構成されます。 |
(C++11以降) |
void f() throw(int); // f()'s set is "int" void g(); // g()'s set is the set of all types struct A { A(); }; // "new A"'s set is the set of all types struct B { B() noexcept; }; // "B()"'s set is empty struct D() { D() throw (double); }; // new D's set is the set of all types
すべての暗黙宣言されたメンバ関数および継承コンストラクタ(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; // exception specification is "noexcept(true)" B(B&&, int = (throw Y(), 0)) noexcept; ~B() throw(Y); }; int n = 7; struct D : public A, public B { // May throw an exception of a type that would match a handler of type // std::bad_array_new_length, but does not throw a bad allocation exception (void*) new (std::nothrow) int[n]; // D may have the following implicitly-declared members: // 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 | 代入と初期化の動作 異なる 例外仕様を持つメンバポインタ間 |
は未指定でした。 制限を適用 関数ポインタに |
| と参照 | C++98 | CWG 973 例外仕様には関数型を含めることができますが、 |
指定された |
| 対応する関数ポインタ変換は指定されていませんでした。 | C++98 | CWG 1330 | 例外仕様は早期にインスタンス化される可能性があります。 |
| 必要に応じてのみインスタンス化されます。 | C++11 | CWG 1267 | 許可されなくなった。 |
| 右辺値参照型は例外仕様で許可されていました。 | C++98 C++11 |
CWG 1351 デフォルト引数(C++98)とデフォルトメンバ初期化子 |
(C++11)は暗黙の例外仕様で無視されました。 |
| 考慮されるようになりました。 | C++11 | CWG 1777 throw(T...) は例外をスローしない |
仕様であり、たとえ T が空のパックであっても 空のパックであれば例外をスローしない。 |
| CWG 2191 | C++98 | typeid 式の潜在的例外のセットスローできない場合でも bad_typeid を含める可能性があります。 |
bad_typeid を含みます。スローできる場合にのみ。 |
[編集] 関連項目
noexcept 指定子(C++11) |
関数が例外をスローする可能性があるかどうかを指定します。 |