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()) }; // true

noexcept演算子は式を実行しない。
コンパイル時に、その式が例外を送出しうるかどうかを調べる。

どんな関数に付けるか

noexceptは、単に「今の実装ではたまたま例外を投げていない」関数に付けるものではない。
その関数が、設計上も例外を外へ出さないことを保証したい場合に付ける。

特にnoexceptが重要なのは、

である。

これらがnoexceptであると、標準ライブラリがより効率のよい実装を選べることがある。

また、デストラクタ(C++)のように、例外を外へ出してはいけない関数でも重要である。

古い例外指定

かつては

void f() throw();
void g() throw(std::out_of_range);

のような動的例外指定が使われていたが、これは非推奨となり、その後のC++で削除された。
現在はnoexceptを使う。

関連

参考