noexcept指定子。
関数が、呼び出し元から見える例外を送出しないことを表すための指定子。 関数の中で例外を投げること自体を文法上禁止するものではない。 ただし、それらの例外が関数の外へ出てはいけない。
概要
C++では関数は、
- 例外を外へ送出しない関数
- 例外を外へ送出する可能性がある関数
のどちらかとして扱われる。
関数を例外を外へ送出しない関数として宣言するには、関数宣言の引数リストの右側にnoexceptを付ける。
void doSomething() noexcept;これはnoexcept(true)と同じ意味である。
一方、noexcept(false)は、その関数が例外を送出する可能性があることを表す。
noexcept違反
noexcept関数から未処理の例外が外へ出ようとすると、std::terminateが呼ばれる。
void f() noexcept
{
throw 1; // 外へ出るとstd::terminate
}この場合、上位に対応するcatchが存在していても、その前にstd::terminateされる。
また、std::terminate時にはstack unwinding(スタック巻き戻し)が行われるかどうかが実装依存になることがある。
noexceptとオーバーロード
例外指定だけが異なる関数をオーバーロードすることはできない。
void f();
void f() noexcept; // エラーnoexcept演算子
noexceptは演算子としても使える。
この場合、式が例外を送出する可能性がないとコンパイラが判断すればtrue、そうでなければfalseになる。
void foo() { throw -1; }
void goo() noexcept {}
constexpr bool b1{ noexcept(5 + 3) }; // true
constexpr bool b2{ noexcept(foo()) }; // false
constexpr bool b3{ noexcept(goo()) }; // truenoexcept演算子は式を実行しない。
コンパイル時に、その式が例外を送出しうるかどうかを調べる。
どんな関数に付けるか
noexceptは、単に「今の実装ではたまたま例外を投げていない」関数に付けるものではない。
その関数が、設計上も例外を外へ出さないことを保証したい場合に付ける。
特にnoexceptが重要なのは、
- ムーブコンストラクタ
- ムーブ代入演算子(C++)
swap関数
である。
これらがnoexceptであると、標準ライブラリがより効率のよい実装を選べることがある。
また、デストラクタ(C++)のように、例外を外へ出してはいけない関数でも重要である。
古い例外指定
かつては
void f() throw();
void g() throw(std::out_of_range);のような動的例外指定が使われていたが、これは非推奨となり、その後のC++で削除された。
現在はnoexceptを使う。