作成日: 2007/05/02

メモ: C++ での例外の指定

概要

とあるソースを読んでいると関数定義の横に throw(A) などと書いてありました (そのソースでは A は例外用のクラスでした) 。 なんじゃこりゃ? と調べてみると、C++ でも関数から投げられる例外を指定しておくことができるようです。 恥ずかしながら知りませんでした。。。

例外指定の構文

投げられる例外を指定する構文は、

throw ( type-id-list )

であり、type-id-list には投げる例外をカンマで区切って指定することができます。

例えば、関数 f() が例外 A を投げられることを指定するには、

class A {};
void f() throw(A);

とします。関数 f() が例外 A、B を投げられることを指定するには、

class A {};
class B {};
void f() throw(A, B);

とします。なお、関数 f() が例外を投げないことを指定するには、

void f() throw();

とします。また、例外指定の無い (従来の) 関数定義は f() がすべての例外を投げられるとみなされます。

void f();

なお、例外を指定したとしても、投げる可能性のある例外を指定しているに過ぎず、 指定していない例外も投げることができます。 そのため、以下のような指定していない例外を投げるコードを書いてもコンパイルエラーとはなりません。

class A {};
class B {};
void f() throw(A)
{
    throw B();  // OK
}
指定していない例外を投げた場合の動作

指定していない例外が投げられたとき、デフォルトでは std::unexpected() が呼び出されます。 std::unexpected() のデフォルトの動作では、最終的に abort() 呼び出しが行われ プログラムが終了することになります。以下のコードを例に取ると、プログラムは何も出力しないまま 終了してしまいます。

#include <cstdio>

class A {};
class B {};
void f() throw(A)
{
    throw B();
}

int main(void)
{
    try {
        f();
    } catch (A) {
        printf("catch A\n");
    } catch (B) {
        printf("catch B\n");
    } catch (...) {
        printf("catch unknown\n");
    }
}

逆に、関数 f() に例外を指定しない以下のようなコードであれば、関数 f() はすべての例外を投げられるとみなされるため、 プログラムは "catch B" を出力します。

#include <cstdio>

class A {};
class B {};
void f()
{
    throw B();
}

int main(void)
{
    try {
        f();
    } catch (A) {
        printf("catch A\n");
    } catch (B) {
        printf("catch B\n");
    } catch (...) {
        printf("catch unknown\n");
    }
}
仮想関数をオーバーライドする場合の例外の指定

仮想関数をオーバーライドする場合、例外の指定は元の仮想関数と同じか、元より厳しい指定でなければなりません。 以下の例では Y::f3()、Y::f4() はともにエラーとなり、コンパイルできません。

class X {
public:
    virtual void f1();              // すべての例外を投げられるとみなされる
    virtual void f2() throw(A, B);  // A または B の例外を投げられる
    virtual void f3() throw(A);     // A の例外のみ投げられる
    virtual void f4() throw();      // 何の例外も投げられない
};

class Y : public X {
public:
    virtual void f1() throw(A);     // A の例外しか投げられないので、元の指定より厳しく OK
    virtual void f2() throw(A);     // A の例外しか投げられないので、元の指定より厳しく OK
    virtual void f3() throw(A, B);  // 元の指定が A の例外しか投げられないのに B の例外も投げようとしているためエラーとなる
    virtual void f4() throw(A);     // 元の指定が何の例外も投げられないのに A の例外を投げようとしているためエラーとなる
};
その他

Microsoft Visual C++ 2005 (以下 VC++ 2005) では

void f() throw(...);

のような構文が使えて、すべての例外を投げられることを指定できます (これが標準 C++ の仕様にあるのかはわかりませんでした。ごめんなさい)。 また、現在のところ、VC++ 2005 では例外の指定が完全には実装されていないため、

class A {};
void f() throw(A);

のように例外を指定しても例外の指定は無視されて (C4290 の警告になります)、

void f() throw(...);

と同じように解釈されてしまいます。

参考文献

プログラミング言語 C++ 第3版 Bjarne Stroustrup 著 株式会社 ロングテール/長尾 高弘 訳
アジソン・ウェスレイ・パブリッシャーズ・ジャパン 株式会社 ISBN4-7561-1895-X