/EH (例外処理モデル)

コンパイラによって生成される例外処理モデルのサポートを指定します。 引数を使って、catch(...) 構文を構造化例外と標準 C++ 例外の両方に対して適用するかどうか、extern "C" コードが例外を throw すると想定するかどうか、および特定の noexcept チェックを最適化により除去するかどうかを指定します。

構文

$
$
$
$

引数

a
標準 C++ スタック アンワインドを有効にします。 catch(...) 構文を使用するときに、構造化 (非同期) 例外と標準 C++ (同期) 例外の両方をキャッチします。 /EHa は、/EHs 引数と /EHc 引数の両方をオーバーライドします。

s
標準 C++ スタック アンワインドを有効にします。 catch(...) 構文を使用するときに、標準 C++ 例外のみをキャッチします。 /EHc も指定しない限り、コンパイラは、extern "C" として宣言された関数が C++ 例外を throw する可能性があると見なします。

c
/EHs と共に使う場合、コンパイラは、extern "C" として宣言された関数が C++ 例外を throw することはないと見なします。 /EHa と共に使っても効果はありません (つまり、/EHca/EHa と同じです)。 /EHs/EHa が指定されていない場合、/EHc は無視されます。

r
すべての noexcept 関数のランタイム終了チェックを常に生成するようにコンパイラに指示します。 既定では、関数が throw しない関数のみを呼び出すとコンパイラが判断した場合、noexcept のランタイム チェックが最適化によって除去されます。 このオプションを使うと、いくつかのコードが追加される代わりに、厳密な C++ 準拠を実現できます。 /EHs/EHa が指定されていない場合、/EHr は無視されます。

-
前のオプションの引数をクリアします。 たとえば、/EHsc-/EHs /EHc- と解釈され、/EHs と等価です。

/EH 引数は、任意の順序で、個別に指定することも組み合わせて指定することもできます。 同じ引数の複数のインスタンスを指定する場合、最後のものが前のすべてをオーバーライドします。 たとえば、/EHr- /EHc /EHs/EHscr-と同じであり、/EHscr- /EHr の効果は /EHscr と同じです。

解説

既定の例外処理動作

コンパイラは、非同期構造化例外処理 (SEH) をサポートするコードを常に生成します。 既定では (つまり、/EHsc/EHs、または /EHa オプションを指定しない場合)、コンパイラではネイティブ C++ catch(...) 句の SEH ハンドラーがサポートされます。 ただし、C++ 例外を部分的にのみサポートするコードも生成します。 既定の例外のアンワインド コードによって、例外のためにスコープ外に出た try ブロックの外の自動 C++ オブジェクトが破棄されることはありません。 C++ の例外が throw された場合、リソース リークと未定義の動作が発生する可能性があります。

標準 C++ 例外処理

スタック オブジェクトを安全にアンワインドする標準 C++ 例外処理モデルの完全なコンパイラ サポートには、/EHsc (推奨)、/EHs、または /EHa が必要です。

/EHs または /EHsc を使用すると、catch(...) 句では非同期構造化例外が catch されません。 アクセス違反とマネージド System.Exception 例外はキャッチされません。 また、非同期例外が発生した場合、スコープ内のオブジェクトは破棄されません。これは、コードによって非同期例外を処理する場合でも同様です。 この動作は、構造化例外を未処理のままにするものです。 代わりに、これらの例外を致命的と見なしてください。

/EHs/EHsc を使う場合、コンパイラは、throw ステートメントか関数呼び出しでしか例外が発生しないと見なします。 この仮定により、コンパイラは多数のアンワインド可能オブジェクトの有効期間を追跡するコードを削除できるため、コード サイズをかなり小さくできます。 /EHa を使うと、コンパイラによって try ブロックが積極的に最適化されないため、実行可能イメージのサイズが大きくなり、速度が低下する可能性があります。 また、コンパイラが C++ 例外を throw する可能性のあるコードを確認しない場合でも、ローカル オブジェクトを自動的にクリーンアップする例外フィルターが除去されません。

構造化例外処理と標準 C++ 例外処理

/EHa コンパイラ オプションを使うと、非同期例外と C++ 例外の両方に対して安全なスタック アンワインドが有効になります。 ネイティブ C++ catch(...) 句を使用して、標準 C++ 例外と構造化例外の両方の処理がサポートされます。 /EHa を指定せずに SEH を実装するには、__try__except__finally の各構文を使用できます。 詳細については、構造化例外処理に関する記事を参照してください。

重要

/EHa を指定して、すべての例外を catch(...) を使って処理しようとすると、危険が生じる可能性があります。 非同期例外のほとんどが回復不能で、致命的であると見なされます。 こうした例外をキャッチして操作を続行すると、プロセスが破損し、検出して修正するのが難しいバグが発生する可能性があります。

Windows と Visual C++ では SEH がサポートされていますが、ISO 標準の C++ 例外処理 (/EHsc または /EHs) を使用することを強くお勧めします。 これにより、コードの移植性と柔軟性が向上します。 それでも、レガシ コードや特定の種類のプログラムで SEH の使用が必要になる場合があるかもしれません。 たとえば、共通言語ランタイム (/clr) をサポートするようにコンパイルされるコードで必要になります。 詳細については、構造化例外処理に関する記事を参照してください。

/EHa を使ってコンパイルしたオブジェクト ファイルは、同じ実行可能モジュールで /EHs または /EHsc を使ってコンパイルしたオブジェクト ファイルにリンクしないことをお勧めします。 モジュール内のどこかで /EHa を使って非同期例外を処理する必要がある場合は、/EHa を使ってモジュール内のすべてのコードをコンパイルします。 構造化例外処理構文は、/EHs を使ってコンパイルするコードと同じモジュール内で使用できます。 ただし、同じ関数内で SEH 構文と C++ の trythrowcatch を混在させることはできません。

throw 以外の方法で発生する例外を catch する場合は、/EHa を使用します。 次の例では、構造化例外が生成され、catch されます。

// compiler_options_EHA.cpp
// compile with: /EHa
#include <iostream>
#include <excpt.h>
using namespace std;

void fail()
{
    // generates SE and attempts to catch it using catch(...)
    try
    {
        int i = 0, j = 1;
        j /= i;   // This will throw a SE (divide by zero).
        printf("%d", j);
    }
    catch(...)
    {
        // catch block will only be executed under /EHa
        cout << "Caught an exception in catch(...)." << endl;
    }
}

int main()
{
    __try
    {
        fail();
    }

    // __except will only catch an exception here
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        // if the exception was not caught by the catch(...) inside fail()
        cout << "An exception was caught in __except." << endl;
    }
}

/clr での例外処理

/clr を使用すると、/EHa が暗黙的に指定されます (つまり、/clr /EHa が冗長になります)。 /EHs または /EHsc/clr の後で使用すると、コンパイラはエラーを生成します。 最適化は、この動作に影響しません。 例外がキャッチされると、例外と同じスコープ内にあるオブジェクトに対して、コンパイラによりクラス デストラクターが呼び出されます。 例外がキャッチされない場合、これらのデストラクターは実行されません。

/clr に基づく例外処理の制約については、「_set_se_translator」を参照してください。

ランタイム例外チェック

/EHr オプションは、noexcept 属性を持つすべての関数でのランタイム終了チェックを強制します。 既定では、関数が "throw しない" 関数のみを呼び出すとコンパイラがバックエンドで判断した場合に、ランタイム チェックが最適化によって除去されます。 throw しない関数とは、属性で例外が throw されないことが指定された関数を指します。 これには、noexceptthrow()__declspec(nothrow) とマークされた関数と、/EHc が指定された場合の extern "C" 関数が含まれます。 throw しない関数には、コンパイラの検査で throw しないと判断された関数も含まれます。 /EHr- を使って明示的に既定の動作を設定することができます。

throw しない属性は、例外が関数によって throw されないことを保証するものではありません。 noexcept 関数の動作とは異なり、MSVC コンパイラは、throw()__declspec(nothrow)、または extern "C" を使って宣言された関数から throw された例外を、未定義の動作と見なします。 これら 3 つの宣言属性を使用する関数では、例外のランタイム終了チェックが強制されません。 noexcept 関数を回避する未処理例外のランタイム チェックを生成するようにコンパイラに強制することによって、/EHr オプションを使ってこの未定義の動作を識別することができます。

Visual Studio またはプログラムでオプション設定する

Visual Studio 開発環境でこのコンパイラ オプションを設定するには

  1. プロジェクトの [プロパティ ページ] ダイアログ ボックスを開きます。 詳細については、Visual Studio での C++ コンパイラとビルド プロパティの設定に関する記事を参照してください。

  2. [構成プロパティ]>[C/C++]>[コード生成] の順に選択します。

  3. [C++ の例外を有効にする] プロパティを変更します。

    または、 [C++ の例外を有効にする][いいえ]に設定してから [コマンド ライン] プロパティ ページをクリックし、 [追加のオプション] ボックスにコンパイラ オプションを追加します。

このコンパイラ オプションをコードから設定するには

関連項目

MSVC コンパイラ オプション
MSVC コンパイラ コマンド ラインの構文
エラーと例外の処理
例外の指定 (throw)
構造化例外処理 (C/C++)