例外指定 (throw, noexcept) (C++)

例外指定は、関数によって伝達できる例外の種類に関するプログラマの意図を示す C++ 言語機能です。 例外指定を使用して、例外によって関数が終了する場合と終了しない場合を指定できます。 コンパイラは、この情報を使用して関数の呼び出しを最適化し、予期しない例外によって関数がエスケープされた場合にプログラムを終了できます。

C++17 以前には 2 種類の例外指定がありました。 noexcept 指定は C++11 の新機能でした。 関数をエスケープできる潜在的な例外のセットが空かどうかを指定します。 動的例外指定 (または throw(optional_type_list) 指定) は C++11 では非推奨になり、throw() (noexcept(true) のエイリアス) を除いて C++17 では削除されました。 この例外指定は、関数からスローできる例外に関する概要情報を提供するように設計されていましたが、実際には、問題があることがわかりました。 ある程度役に立つことがわかっている動的例外の指定は、無条件の throw() の指定でした。 たとえば、関数宣言は次のようになります。

void MyFunction(int i) throw();

このコードでは、コンパイル時に関数が例外をスローしません。 ただし、/std:c++14 モードでは、関数が例外をスローすると、動作が不定になる可能性があります。 そのため、上記の演算子の代わりに noexcept 演算子を使用することをお勧めします。

void MyFunction(int i) noexcept;

次の表は、Microsoft C++ での例外の指定の実装をまとめたものです。

例外の指定 意味
noexcept
noexcept(true)
throw()
関数は例外をスローしません。 /std:c++14 モード (既定)、noexcept および noexcept(true) は同等です。 noexcept または noexcept(true) が宣言されている関数から例外がスローされると、std::terminate が呼び出されます。 /std:c++14 モードで throw() として宣言された関数から例外がスローされると、結果は不定の動作になります。 特定の関数は呼び出されません。 これは、コンパイラが std::unexpected を呼び出す必要がある C++14 標準とは異なっています。
Visual Studio 2017 バージョン 15.5 以降: /std:c++17 モードでは、noexceptnoexcept(true)、および throw() はすべて同等です。 /std:c++17 モードでは、throw()noexcept(true) の別名です。 /std:c++17 モード以降では、これらの指定で宣言された関数から例外がスローされると、C++17 標準の必要に応じて std::terminate が呼び出されます。
noexcept(false)
throw(...)
指定なし
関数は任意の型の例外をスローできます。
throw(type) (C++14 以前) 関数は type 型の例外をスローできます。 コンパイラは構文を受け入れますが、それを noexcept(false) として解釈します。 /std:c++17 モード以降では、コンパイラは警告 C5040 を発行します。

アプリケーションで例外処理を使用する場合は、noexceptnoexcept(true)、または throw() とマークされた関数の外側のスコープを終了する前に、スローされた例外を処理する関数が呼び出し履歴に存在する必要があります。 例外をスローする関数と例外を処理する関数の間で呼び出された関数が noexceptnoexcept(true) (または /std:c++17 モードでは throw()) として指定されている場合、noexcept 関数が例外を伝達するとプログラムは終了します。

関数の例外動作は、次の要因によって決まります。

  • どの言語標準コンパイル モードが設定されているか。

  • C または C++ で関数をコンパイルするかどうか。

  • どの /EH コンパイラ オプションを使用するか。

  • 例外の指定を明示的に使用するかどうか。

明示的な例外の指定は C 関数では使用できません。 C 関数は、/EHsc で例外をスローしないと見なされ、/EHs/EHa、または /EHac の下で構造化例外をスローする可能性があります。

次の表は、C++ 関数がさまざまなコンパイラ例外処理オプションの下でスローされる可能性があるかどうかをまとめたものです。

機能 /EHsc /EHs /EHa /EHac
例外を指定していない C++ 関数 はい イエス イエス はい
noexceptnoexcept(true)、または throw() 例外の指定を含む C++ 関数 いいえ 番号 イエス はい
noexcept(false)throw(...)、または throw(type) 例外の指定を含む C++ 関数 はい イエス イエス はい

// exception_specification.cpp
// compile with: /EHs
#include <stdio.h>

void handler() {
   printf_s("in handler\n");
}

void f1(void) throw(int) {
   printf_s("About to throw 1\n");
   if (1)
      throw 1;
}

void f5(void) throw() {
   try {
      f1();
   }
   catch(...) {
      handler();
    }
}

// invalid, doesn't handle the int exception thrown from f1()
// void f3(void) throw() {
//   f1();
// }

void __declspec(nothrow) f2(void) {
   try {
      f1();
   }
   catch(int) {
      handler();
    }
}

// only valid if compiled without /EHc
// /EHc means assume extern "C" functions don't throw exceptions
extern "C" void f4(void);
void f4(void) {
   f1();
}

int main() {
   f2();

   try {
      f4();
   }
   catch(...) {
      printf_s("Caught exception from f4\n");
   }
   f5();
}
About to throw 1
in handler
About to throw 1
Caught exception from f4
About to throw 1
in handler

関連項目

trythrow、および catch ステートメント (C++)
例外とエラー処理に関する最新の C++ のベスト プラクティス