noexcept 指定子 (C++11 以降)
関数が例外をスローするかどうかを指定します。
目次 |
[編集] 構文
noexcept
|
(1) | ||||||||
noexcept(式) |
(2) | ||||||||
throw()
|
(3) | (C++17で非推奨) (C++20で削除) | |||||||
noexcept(true) と同じnoexcept に続く ( は常にこの形式の一部です(初期化子を開始することはありません)。| 式 | - | 型 bool の文脈的に変換された定数式 |
[編集] 説明
|
noexcept-specification は関数の型の一部ではなく (動的例外指定と同様)、関数、変数、関数型の非静的データメンバー、関数へのポインタ、関数への参照、またはメンバー関数へのポインタを宣言する際のラムダ宣言子またはトップレベルの関数宣言子の一部としてのみ現れることができます。また、それらの宣言のいずれかで、結果的に関数へのポインタまたは参照となるパラメータまたは戻り値を宣言する場合にも現れることができます。typedef または型エイリアス宣言には現れることはできません。 void f() noexcept; // the function f() does not throw void (*fp)() noexcept(false); // fp points to a function that may throw void g(void pfa() noexcept); // g takes a pointer to function that doesn't throw // typedef int (*pf)() noexcept; // error |
(C++17まで) |
|
noexcept-specification は関数の型の一部であり、任意の関数宣言子の一部として現れることがあります。 |
(C++17以降) |
C++ のすべての関数は、非スローであるか、潜在的にスローであるかのどちらかです。
- 潜在的にスローする関数は次のとおりです。
|
(C++17まで) |
- 式が
falseに評価されるnoexcept指定子で宣言された関数 - 以下のものを除く、
noexcept指定子なしで宣言された関数
- デストラクタ。ただし、潜在的に構築される基底またはメンバーのデストラクタが潜在的にスローである場合を除く (下記参照)
- デフォルトコンストラクタ、コピーコンストラクタ、ムーブコンストラクタが暗黙的に宣言されるか、最初の宣言でデフォルト指定された場合。ただし、以下の場合は除く。
- コンストラクタの暗黙の定義が呼び出す基底またはメンバーのコンストラクタが潜在的にスローである場合 (下記参照)
- デフォルト引数式など、そのような初期化の副式が潜在的にスローである場合 (下記参照)
- デフォルトメンバ初期化子 (デフォルトコンストラクタの場合のみ) が潜在的にスローである場合 (下記参照)
- 式が
|
(C++20以降) |
- 非スロー関数とは、その他のすべての関数です (noexcept 指定子の式が
trueに評価される関数、デストラクタ、デフォルト化された特殊メンバ関数、およびデアロケーション関数など)。
明示的なインスタンス化では noexcept 指定子を使用できますが、必須ではありません。使用する場合、例外指定は他のすべての宣言と同じでなければなりません。単一の翻訳単位内で例外指定が同じでない場合にのみ診断が必須となります。
例外指定のみが異なる関数はオーバーロードできません (戻り値の型と同様に、例外指定は関数型の一部ですが、関数シグネチャの一部ではありません)(C++17 以降)。
void f() noexcept; void f(); // error: different exception specification void g() noexcept(false); void g(); // ok, both declarations for g are potentially-throwing
非スロー関数へのポインタ (メンバー関数へのポインタを含む) は、潜在的にスローする関数へのポインタに 代入または初期化に使用できますが(C++17 まで) 暗黙的に変換可能ですが(C++17 以降)、その逆はできません。
void ft(); // potentially-throwing void (*fn)() noexcept = ft; // error
仮想関数が非スローである場合、すべてのオーバーライダのすべての宣言(定義を含む)も、そのオーバーライダが削除済みとして定義されていない限り、非スローでなければなりません。
struct B { virtual void f() noexcept; virtual void g(); virtual void h() noexcept = delete; }; struct D: B { void f(); // ill-formed: D::f is potentially-throwing, B::f is non-throwing void g() noexcept; // OK void h() = delete; // OK };
非スロー関数は、潜在的にスローする関数を呼び出すことが許可されています。例外がスローされ、ハンドラの検索が非スロー関数の最外ブロックに到達すると、関数std::terminateが呼び出されます。
extern void f(); // potentially-throwing void g() noexcept { f(); // valid, even if f throws throw 42; // valid, effectively a call to std::terminate }
関数テンプレート特殊化の例外指定は、関数宣言と共にインスタンス化されません。それは(下記で定義されるように)必要な場合にのみインスタンス化されます。
暗黙的に宣言された特殊メンバ関数の例外指定も、必要になったときにのみ評価されます(特に、派生クラスのメンバ関数の暗黙的宣言は、基底メンバ関数の例外指定のインスタンス化を必要としません)。
関数テンプレートの特殊化の noexcept-specification が必要であるが、まだインスタンス化されていない場合、依存名は検索され、式で使用されているすべてのテンプレートは、特殊化の宣言の場合と同様にインスタンス化されます。
関数の noexcept-specification は、以下のコンテキストで必要とみなされます。
- オーバーロード解決によって関数が選択される式において
- 関数がodr-usedされる場合
- 関数が odr-used されるはずだが、未評価のオペランドに現れる場合
template<class T> T f() noexcept(sizeof(T) < 4); int main() { decltype(f<void>()) *p; // f unevaluated, but noexcept-spec is needed // error because instantiation of the noexcept specification // calculates sizeof(void) }
- 別の関数宣言と比較するために指定が必要な場合 (例: 仮想関数のオーバーライダや関数テンプレートの明示的な特殊化)
- 関数定義において
- デフォルト化された特殊メンバ関数が自身の例外指定を決定するためにそれをチェックする必要があるため、指定が必要な場合 (これは、デフォルト化された特殊メンバ関数自体の指定が必要な場合にのみ発生します)。
潜在的にスローする式の正式な定義(上記のようにデストラクタ、コンストラクタ、代入演算子のデフォルト例外指定を決定するために使用されます)
式 e が潜在的にスローであるのは、以下のいずれかの条件を満たす場合です。
-
eが、潜在的にスローする関数、関数へのポインタ、またはメンバ関数へのポインタの関数呼び出しである場合。ただし、eがコア定数式である場合を除く(C++17 まで) -
eが潜在的にスローする関数への暗黙の呼び出しを行う場合 (オーバーロードされた演算子、new-expression のアロケーション関数、関数引数のコンストラクタ、またはeが完全式の場合はデストラクタなど) -
eがthrow-expressionである場合 -
eが多態的な参照型をキャストするdynamic_castである場合 -
eが多態的な型への参照解除されたポインタに適用されるtypeid式である場合 -
eが潜在的にスローする直接の副式を持つ場合
struct A { A(int = (A(5), 0)) noexcept; A(const A&) noexcept; A(A&&) noexcept; ~A(); }; struct B { B() throw(); B(const B&) = default; // implicit exception specification is noexcept(true) B(B&&, int = (throw Y(), 0)) noexcept; ~B() noexcept(false); }; int n = 7; struct D : public A, public B { int * p = new int[n]; // D::D() potentially-throwing because of the new operator // D::D(const D&) non-throwing // D::D(D&&) potentially-throwing: the default argument for B’s constructor may throw // D::~D() potentially-throwing // note; if A::~A() were virtual, this program would be ill-formed because an overrider // of a non-throwing virtual cannot be potentially-throwing };
[編集] ノート
定数式の使用法の1つは、(noexcept 演算子と共に) 一部の型には noexcept を宣言し、他の型には宣言しない関数テンプレートを定義することです。
関数に対する noexcept 指定はコンパイル時のチェックではないことに注意してください。これは単に、関数が例外をスローするかどうかをプログラマーがコンパイラに通知する方法です。コンパイラはこの情報を使用して、非スロー関数に対する特定の最適化を有効にしたり、特定の式が例外をスローすると宣言されているかどうかをコンパイル時にチェックできるnoexcept 演算子を有効にしたりすることができます。たとえば、std::vector などのコンテナは、要素のムーブコンストラクタが noexcept であれば要素をムーブし、そうでなければコピーします (ただし、コピーコンストラクタがアクセス不可能で、潜在的にスローするムーブコンストラクタがアクセス可能な場合は、強力な例外保証は放棄されます)。
[編集] 非推奨
noexcept は C++11 で非推奨となったthrow()の改良版です。C++17 以前のthrow()とは異なり、noexcept はstd::unexpectedを呼び出さず、スタックをアンワインドするかどうかは不定であり、std::terminateを呼び出します。これにより、コンパイラはthrow()の実行時オーバーヘッドなしでnoexceptを実装できる可能性があります。C++17以降、throw()はnoexcept(true)と完全に同等であると再定義されています。
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_noexcept_function_type |
201510L |
(C++17) | 例外指定を型システムの一部にする |
[編集] キーワード
noexcept, throw(C++17 以降)(C++20 まで)
[編集] 例
// whether foo is declared noexcept depends on if the expression // T() will throw any exceptions template<class T> void foo() noexcept(noexcept(T())) {} void bar() noexcept(true) {} void baz() noexcept { throw 42; } // noexcept is the same as noexcept(true) int main() { foo<int>(); // noexcept(noexcept(int())) => noexcept(true), so this is fine bar(); // fine baz(); // compiles, but at runtime this calls std::terminate }
[編集] 欠陥レポート
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 1330 | C++11 | 例外指定が熱心にインスタンス化される可能性がある | 必要に応じてのみインスタンス化される |
| CWG 1740 | C++11 | ( が noexcept の後に初期化子を開始する可能性がある |
それの一部にしかありえない noexcept指定 |
| CWG 2039 | C++11 | 変換前の式のみが定数である必要がある | 変換も定数式で有効でなければならない 定数式で有効 |
[編集] 関連項目
noexcept 演算子(C++11) |
式が例外をスローするかどうかを決定する |
| 動的例外指定(C++17 まで) | 関数がスローする例外を指定する (C++11 で非推奨) |
throw 式
|
エラーを通知し、エラーハンドラに制御を転送する |
| (C++11) |
ムーブコンストラクタが例外を投げない場合に引数をxvalueに変換する (関数テンプレート) |