noexcept 指定子 (C++11以上)
関数が例外を投げ得るかどうかを指定します。
目次 |
[編集] 構文
noexcept
|
(1) | ||||||||
noexcept( expression)
|
(2) | ||||||||
throw()
|
(3) | (非推奨)(C++20で削除) | |||||||
noexcept
(
true
)
と同じです。
3) noexcept(true) と同じです。
|
(C++17以上) (C++20未満) |
(C++17未満) |
expression | - | bool 型の文脈的に変換された定数式 |
[編集] 説明
noexcept 指定は関数の型の一部であり、任意の関数宣言子の一部として現れることができます。 |
(C++17以上) |
noexcept 指定は (動的例外指定と同様に) 関数の型の一部ではなく、ラムダ宣言子または関数、変数、関数型の非静的データメンバ、関数へのポインタ、関数への参照またはメンバ関数へのポインタを宣言するときおよび関数へのポインタまたは参照となるときのそれらの宣言のいずれかにおいて引数または戻り値の型を宣言するときのトップレベルの関数宣言子の一部としてのみ現れることができます。 typedef または型エイリアス宣言で現れることはできません。 void f() noexcept; // 関数 f() は例外を投げません。 void (*fp)() noexcept(false); // fp は例外を投げるかもしれない関数を指します。 void g(void pfa() noexcept); // g は例外を投げない関数へのポインタを受け取ります。 // typedef int (*pf)() noexcept; // エラー。 |
(C++17未満) |
C++ のすべての関数は例外を投げないか潜在的に例外を投げるかのいずれかです。
- 潜在的に例外を投げる関数は以下のいずれかです。
|
(C++17未満) |
- expression が
false
に評価される noexcept 指定子付きで宣言された関数。 - noexcept 指定子なしで宣言された関数。 ただし以下を除きます。
- デストラクタ。 ただし潜在的に構築される基底またはメンバのいずれかのデストラクタが潜在的に例外を投げる場合は除きます (後述)。
- 最初の宣言において暗黙に宣言されたまたはデフォルト化されたデフォルトコンストラクタ、コピーコンストラクタ、ムーブコンストラクタ。 ただし以下を除きます。
- コンストラクタの暗黙の定義が呼ぶ基底またはメンバに対するコンストラクタが潜在的に例外を投げる (後述)。
- 初期化の部分式 (デフォルト引数式など) が潜在的に例外を投げる (後述)。
- デフォルトメンバ初期化子 (デフォルトコンストラクタの場合のみ) が潜在的に例外を投げる (後述)。
- expression が
|
(C++20以上) |
- 解放関数。
- 例外を投げない関数はそれ以外の全部です (expression が
true
に評価される noexcept 指定子付きの関数、デストラクタ、デフォルト化された特別なメンバ関数および解放関数)。
明示的実体化は noexcept 指定子を使用しても構いません (が要求はされません)。 使用する場合、例外指定は他のすべての宣言に対するものと同じでなければなりません。 診断は例外指定が単一翻訳単位内で同じでない場合にのみ要求されます。
例外指定だけが異なる関数はオーバーロードできません (戻り値と同様に、例外指定は関数の型の一部ですが、関数のシグネチャの一部ではありません) (C++17以上)。
void f() noexcept; void f(); // エラー、例外指定が異なります。 void g() noexcept(false); void g(); // OK、 g に対する両方の宣言が潜在的に例外を投げます。
例外を投げない関数へのポインタは潜在的に例外を投げる関数へのポインタに暗黙に変換可能 (C++17以上)代入可能 (C++17未満)ですが、その逆はできません。
void ft(); // 潜在的に例外を投げます。 void (*fn)() noexcept = ft; // エラー。
仮想関数が例外を投げない場合は、すべてのオーバーライドのすべての宣言 (定義を含む) も同様に例外を投げないでなければなりません。 ただしオーバーライドが削除されたものとして定義される場合は除きます。
struct B { virtual void f() noexcept; virtual void g(); virtual void h() noexcept = delete; }; struct D: B { void f(); // ill-formed、 D::f は潜在的に例外を投げ、 B::f は例外を投げません。 void g() noexcept; // OK void h() = delete; // OK };
例外を投げない関数は潜在的に例外を投げる関数を呼ぶことが許されます。 例外が投げられ、ハンドラの検索が例外を投げない関数の最も外側のブロックに遭遇した場合は、関数 std::terminate が呼ばれます。
extern void f(); // 潜在的に例外を投げます。 void g() noexcept { f(); // 有効 (たとえ f が例外を投げる可能性があっても)。 throw 42; // 有効 (実質的に std::terminate を呼びます)。 }
関数テンプレートの特殊化の noexcept 指定は、関数宣言と一緒には実体化されません。 必要なとき (下で定義される通り) にのみ実体化されます。 暗黙に宣言される特別なメンバ関数の例外指定も、必要なときにのみ評価されます (特に、派生クラスのメンバ関数の暗黙の宣言は、実体化される基底メンバ関数の例外指定を要求しません)。 関数テンプレートの特殊化の noexcept 指定が必要だけれどもまだ実体化されていない場合は、依存名が名前探索され、その特殊化の宣言に対するかのように、 expression で使用されるあらゆるテンプレートが実体化されます。 関数の noexcept 指定は、以下の文脈において、必要であるとみなされます。
template<class T> T f() noexcept(sizeof(T) < 4); int main() { decltype(f<void>()) *p; // f は未評価ですが、 noexcept 指定は必要です。 // noexcept 指定の実体化は sizeof(void) を計算するため // エラーです。 }
|
(C++14以上) |
潜在的に例外を投げる関数の形式的な定義は以下の通りです (上で説明した通り、デストラクタ、コンストラクタおよび代入演算子のデフォルトの例外指定を決定するために使用されます)。
動的例外指定を参照してください。 |
(C++17未満) |
以下の場合、式
struct A { A(int = (A(5), 0)) noexcept; A(const A&) noexcept; A(A&&) noexcept; ~A(); }; struct B { B() throw(); B(const B&) = default; // 暗黙の例外指定は 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() は new 演算子のため潜在的に例外を投げます。 // D::D(const D&) は例外を投げません。 // D::D(D&&) は潜在的に例外を投げます。 B のコンストラクタに対するデフォルト引数が例外を投げる可能性があります。 // D::~D() は潜在的に例外を投げます。 // 注: もし A::~A() が仮想であったならば、例外を投げない仮想関数のオーバーライドは // 潜在的に例外を投げるであれないため、プログラムは ill-formed であったでしょう。 }; |
(C++17以上) |
[編集] ノート
定数式 expression の使用方法のひとつは (noexcept
演算子と共に) 何らかの型に対しては noexcept
を宣言するけれどもそれ以外に対してはそうでない関数テンプレートを定義することです。
関数に対する noexcept
指定はコンパイル時のチェックではないことに注意してください。 単に関数が例外を投げるであろうか否かをプログラマがコンパイラに伝える方法なだけです。 コンパイラは例外を投げない関数に対する特定の最適化を有効にするためにこの情報を使用することができ、特定の式が何らかの例外を投げると宣言されているかどうかコンパイル時にチェックできる noexcept
演算子を有効化できます。 例えば、 std::vector のようなコンテナは、要素のムーブコンストラクタが noexcept
であれば要素をムーブし、そうでなければコピーします (コピーコンストラクタがアクセス可能でないけれども潜在的に例外を投げるムーブコンストラクタがアクセス可能である場合は除きます。 この場合は強い例外保証は断念されます)。
[編集] 非推奨
noexcept
は、 C++11 で非推奨になったthrow() の、改善版です。 C++17 未満の throw() と異なり、 noexcept
は std::unexpectedを呼ばず、スタックを巻き戻すかもしれないし巻き戻さないかもしれません (これはコンパイラが throw() の実行時オーバーヘッドなしに noexcept
を実装することを潜在的に可能にします)。 C++17 以降、 throw() は noexcept(true) と正確に同等であると再定義されました。
[編集] 欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 2039 | C++11 | only the expression before conversion is required to be constant | the conversion must also be valid in a constant expression |
[編集] キーワード
[編集] 例
// foo が noexcept 宣言されるかどうかは、 // 式 T() が何らかの例外を投げるかどうかによります。 template <class T> void foo() noexcept(noexcept(T())) {} void bar() noexcept(true) {} void baz() noexcept { throw 42; } // noexcept は noexcept(true) と同じです。 int main() { foo<int>(); // noexcept(noexcept(int())) → noexcept(true) のため、これは OK です。 bar(); // OK baz(); // コンパイルできますが、実行時に std::terminate を呼びます。 }
[編集] 関連項目
noexcept 演算子(C++11)
|
式が何らかの例外を投げるかどうか調べます |
動的例外指定(C++17未満) | 何の例外が関数によって投げられるかを指定します (C++11で非推奨) |
throw 式 | エラーを通知し、制御をエラーハンドラに移します |
(C++11) |
ムーブコンストラクタが例外を投げない場合、右辺値参照を取得します (関数テンプレート) |