129

C++ では、例外指定子を使用して、関数が例外をスローするかどうかを指定できます。例えば:

void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type

次の理由から、実際にそれらを使用することに疑問があります。

  1. コンパイラは厳密な方法で例外指定子を実際に強制するわけではないため、メリットは大きくありません。理想的には、コンパイル エラーを取得したいと考えています。
  2. 関数が例外指定子に違反した場合、標準的な動作はプログラムを終了することだと思います。
  3. VS.Net では、throw(X) を throw(...) として扱うため、標準への準拠は強くありません。

例外指定子を使用する必要があると思いますか?
「はい」または「いいえ」で答え、その理由を説明してください。

4

14 に答える 14

101

いいえ。

いくつかの例を次に示します。

  1. 例外仕様でテンプレートコードが書けず、

    template<class T>
    void f( T k )
    {
         T x( k );
         x.x();
    }
    

    コピーがスローされる可能性があり、パラメーターの受け渡しがスローされる可能性があり、x()未知の例外がスローされる可能性があります。

  2. 例外仕様は拡張性を妨げる傾向があります。

    virtual void open() throw( FileNotFound );
    

    に進化するかもしれません

    virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );
    

    あなたは本当にそれを次のように書くことができます

    throw( ... )
    

    1 つ目は拡張可能ではなく、2 つ目は野心的であり、3 つ目は、仮想関数を作成するときに実際に意味するものです。

  3. レガシーコード

    別のライブラリに依存するコードを書くとき、何かがひどくうまくいかないときにそれがどうなるか本当にわかりません。

    int lib_f();
    
    void g() throw( k_too_small_exception )
    { 
       int k = lib_f();
       if( k < 0 ) throw k_too_small_exception();
    }
    

    glib_f()スローすると終了します。これは (ほとんどの場合) 本当に必要なものではありません。std::terminate()決して呼び出されるべきではありません。静かに/暴力的に死ぬよりも、スタックトレースを取得できる未処理の例外でアプリケーションをクラッシュさせる方が常に良いです。

  4. 一般的なエラーを返し、例外的な場合にスローするコードを記述します。

    Error e = open( "bla.txt" );
    if( e == FileNotFound )
        MessageUser( "File bla.txt not found" );
    if( e == AccessDenied )
        MessageUser( "Failed to open bla.txt, because we don't have read rights ..." );
    if( e != Success )
        MessageUser( "Failed due to some other error, error code = " + itoa( e ) );
    
    try
    {
       std::vector<TObj> k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    { 
       MessageUser( "out of memory, exiting process" );
       throw;
    }
    

それにもかかわらず、ライブラリが独自の例外をスローするだけの場合は、例外仕様を使用して意図を示すことができます。

于 2008-09-18T00:26:36.477 に答える
42

C++ での例外指定は避けてください。あなたが質問で与えた理由は、その理由のかなり良い出発点です。

Herb Sutter の「A Pragmatic Look at Exception Specifications」を参照してください。

于 2008-09-17T23:21:40.283 に答える
14

標準の例外規則 (C++ の場合)
例外指定子は、C++ 標準での実験であり、ほとんど失敗したと思います。
例外として、no throw 指定子は便利ですが、適切な try catch ブロックを内部的に追加して、コードが指定子と一致することを確認する必要があります。Herb Sutter には、このテーマに関するページがあります。ゴッチ 82

さらに、例外保証について説明する価値があると思います。

これらは基本的に、オブジェクトのメソッドをエスケープする例外によってオブジェクトの状態がどのように影響を受けるかについてのドキュメントです。残念ながら、それらは強制されていないか、コンパイラによって言及されていません。
ブーストと例外

例外保証

保証なし:

例外がメソッドをエスケープした後のオブジェクトの状態についての保証はありません
。このような状況では、オブジェクトを使用しないでください。

基本保証:

ほとんどすべての状況で、これはメソッドが提供する最低限の保証です。
これにより、オブジェクトの状態が適切に定義され、一貫して使用できることが保証されます。

強力な保証: (別名トランザクション保証)


これにより、メソッドが正常に完了するか、例外がスローされてオブジェクトの状態が変化しないことが保証されます。

スロー保証なし:

このメソッドは、例外がメソッドの外に伝播できないことを保証します。
すべてのデストラクタはこれを保証する必要があります。
| | 注: 例外が既に伝搬している間に、例外がデストラクタをエスケープした場合
| アプリケーションが終了します

于 2008-09-18T03:38:37.047 に答える
8

例外仕様に違反すると、gcc は警告を発します。私がしていることは、マクロを使用して例外仕様を「リント」モードでのみ使用し、例外が私のドキュメントと一致することを確認するために明示的にコンパイルすることです。

于 2008-09-18T03:26:36.903 に答える
7

唯一の有用な例外指定子は、"doesn't throw" のように "throw()" です。

于 2008-09-18T20:29:26.790 に答える
4

例外指定は、C++ ではそれほど便利なツールではありません。ただし、std::unexpected と組み合わせると、/is/ 良い使い方があります。

私が一部のプロジェクトで行っているのは、例外仕様を含むコードであり、それから set_unexpected() を呼び出して、独自の設計の特別な例外をスローする関数を呼び出します。この例外は、構築時に (プラットフォーム固有の方法で) バックトレースを取得し、std::bad_exception から派生します (必要に応じて伝播できるようにするため)。通常のように terminate() 呼び出しが発生した場合、バックトレースは what() によって出力されます (それを引き起こした元の例外も同様です。見つけるのは難しいことではありません)。予期しないライブラリ例外がスローされたなどの違反。

これを行うと、ライブラリ例外 (std 例外を除く) の伝播を許可せず、すべての例外を std::exception から派生させます。ライブラリがスローすることを決定した場合は、キャッチして独自の階層に変換し、コードを常に制御できるようにします。依存関数を呼び出すテンプレート化された関数は、明らかな理由から例外指定を避ける必要があります。ただし、ライブラリ コードとのテンプレート化された関数インターフェイスを持つことはめったにありません (実際に有用な方法でテンプレートを使用するライブラリはほとんどありません)。

于 2008-10-20T19:17:40.467 に答える
3

「throw()」仕様を使用すると、関数が例外をスローしないことがわかっている場合(または、少なくとも例外をスローしないことを約束している場合)、コンパイラはコードフロー分析を実行するときにいくつかの最適化を実行できます。Larry Ostermanは、これについてここで簡単に説明しています。

http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx

于 2010-03-01T15:13:31.590 に答える
3

いいえ。それらを使用し、コードまたはコードによって呼び出されたコードのいずれかによって指定しなかった例外がスローされた場合、デフォルトの動作はプログラムを即座に終了することです。

また、C++0x標準の現在のドラフトではそれらの使用は非推奨になっていると思います。

于 2008-09-19T01:49:04.413 に答える
3

周りのコメントよりも関数宣言を見たい人が使用するコードを書いている場合、仕様はキャッチしたい例外を伝えます。

throw()それ以外の場合、例外をスローしないことを示す以外に、何かを使用することは特に有用ではありません。

于 2008-09-17T23:13:54.500 に答える
2

通常、例外指定子は使用しません。ただし、問題の関数から他の例外が発生し、プログラムが決定的に修正できない場合は、それが役立つ可能性があります。いずれの場合も、その関数から予想される例外を明確に文書化してください。

はい、例外指定子を持つ関数からスローされる未指定の例外の予想される動作は、terminate() を呼び出すことです。

また、Scott Meyers が「より効果的な C++」でこの問題に取り組んでいることにも注目します。彼の『Effective C++』と『More Effective C++』は強く推奨される本です。

于 2008-09-17T23:12:59.617 に答える
2

はい、内部文書に興味がある場合は。または、他の人が使用するライブラリを作成して、ドキュメントを参照せずに何が起こるかを伝えることができます。スローするかしないかは、API の一部と見なすことができ、戻り値とほとんど同じです。

私は同意します、それらはコンパイラで正確なJavaスタイルを強制するのに実際には役に立ちませんが、何もないか、でたらめなコメントよりはましです。

于 2008-09-17T23:17:13.243 に答える
2

これらは単体テストに役立つ可能性があるため、テストを作成するときに関数が失敗したときに何をスローするかを知ることができますが、コンパイラでそれらを取り巻く強制はありません。C++ では不要な余分なコードだと思います。どちらを選択しても、コードが読みやすいままになるように、プロジェクトとチーム メンバー全体で同じコーディング標準に従っていることを確認してください。

于 2008-09-17T23:26:58.773 に答える
-2

例外仕様 = くだらない、30 歳以上の Java 開発者に聞いてください

于 2008-10-20T19:22:28.850 に答える