1. ホーム
  2. c++

[解決済み] C++で例外指定子を使うべきですか?

2022-07-15 16:43:34

質問

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(...)として扱うので、標準への固執が強くない。

例外指定子は使うべきと思いますか?

yes"またはno"で答え、あなたの答えを正当化する理由をいくつか提示してください。

どのように解決するのですか?

いいえ。

その理由をいくつかの例で紹介します。

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

    g が終了するのは lib_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;
    }
    
    

とはいえ、ライブラリが独自の例外を投げるだけの場合、例外指定を使って意図を示すことができます。