C++ には、特定の関数が、指定されたリストの例外だけをスローするように限定するメカニズムがあります。
任意の関数の先頭に例外を指定すると、関数の呼び出し元に対して、その関数が例外の指定に含まれていない例外を
直接にも間接にもスローしないことを保証することができます。
例えば、次の関数を考えます。
void translate() throw(unknown_word,bad_grammar) { /* ... */ }
これは、型が unknown_word または bad_grammar である例外オブジェクト、 あるいは unknown_word または bad_grammar から派生した型の例外オブジェクトのみをスローすることを明示的に示しています。
構文 - 例外指定 >>-throw--(--+--------------+--)------------------------------->< '-type_id_list-'
type_id_list は、コンマで区切られた型のリストです。 このリストでは、オプションで const または volatile、あるいはその両方で修飾した、非完了型、 および非完了型を指すポインターまたは参照 (void を指すポインターを除く) を指定することはできません。 例外指定では、型を定義できません。
例外指定のない関数は、すべての例外のスローを認めます。 空の type_id_list を持つ例外指定を使用する関数、throw() は、例外のスローを許可しません。
例外指定は関数の型の一部ではありません。
例外指定は、関数、関数を指すポインター、関数への参照、メンバー関数宣言を指すポインター、または メンバー関数定義を指すポインターの関数宣言子の終了にのみ現れます。 例外指定を typedef 宣言に入れることはできません。 次の宣言は、このことを示しています。
void f() throw(int); void (*g)() throw(int); void h(void i() throw(int)); // typedef int (*j)() throw(int); This is an error.
コンパイラーは、最後の宣言、typedef int (*j)() throw(int) を許可しません。
クラス A が、関数の例外指定の type_id_list に入っている型の一つだとします。 その関数は、クラス A またはクラス A から public に派生したクラスの 例外オブジェクトをスローできます。次の例は、このことを示しています。
class A { }; class B : public A { }; class C { }; void f(int i) throw (A) { switch (i) { case 0: throw A(); case 1: throw B(); default: throw C(); } } void g(int i) throw (A*) { A* a = new A(); B* b = new B(); C* c = new C(); switch (i) { case 0: throw a; case 1: throw b; default: throw c; } }
関数 f() は、型 A または B のオブジェクトをスローできます。 関数が型 C のオブジェクトをスローしようとする場合、 コンパイラーは、型 C が関数の例外指定に指定されておらず、A から public に派生したものでもないので、unexpected() を呼び出します。 同様に、関数 g() は、型 C のオブジェクトを指すポインターをスローできません。 この関数は、型 A のポインター、 または A から public に派生するオブジェクトのポインターをスローすることができます。
仮想関数をオーバーライドする関数は、仮想関数が指定する例外だけをスローできます。 次の例は、このことを示しています。
class A { public: virtual void f() throw (int, char); }; class B : public A{ public: void f() throw (int) { } }; /* The following is not allowed. */ /* class C : public A { public: void f() { } }; class D : public A { public: void f() throw (int, char, double) { } }; */
コンパイラーは、メンバー関数が、型 int の例外のみをスローするので、B::f() を認めます。 コンパイラーは、メンバー関数が、どの種類の例外もスローするので、C::f() を許可しません。 コンパイラーは、メンバー関数が、A::f() よりも多くの型の例外 (int、char、および double) を スローするので、D::f() を許可しません。
x という名前の関数を指すポインター、または y という名前の関数を指すポインターの 割り当て、または初期化を行うとします。 関数 x を指すポインターは、y の例外指定が指定する例外のみをスローできます。 次の例は、このことを示しています。
void (*f)(); void (*g)(); void (*h)() throw (int); void i() { f = h; // h = g; This is an error. }
コンパイラーは、f がどのような種類の例外もスローできるので、割り当て f = h を認めます。 g は、どのような種類の例外もスローできますが、h が、型 int のオブジェクトしかスローできないので、コンパイラーは、割り当て h = g を許可しません。
暗黙的に宣言された特別なメンバー関数は (デフォルトのコンストラクター、コピー・コンストラクター、デストラクター、 およびコピー代入演算子)、例外指定を持っています。 暗黙的に宣言された特別なメンバー関数は、自身の例外指定に、その特別な関数が起動する関数の例外指定に宣言された型を取り込みます。 特別な関数を起動する関数が、例外をすべて許可する場合は、特別な関数も例外をすべて許可します。 特別な関数が呼び出す関数のすべてが、例外を許可しない場合は、特別な関数も例外を許可しません。 次の例は、このことを示しています。
class A { public: A() throw (int); A(const A&) throw (float); ~A() throw(); }; class B { public: B() throw (char); B(const A&); ~B() throw(); }; class C : public B, public A { };
上記の例で示した次の特別な関数は、暗黙的に宣言されています。
C::C() throw (int, char); C::C(const C&); // Can throw any type of exception, including float C::~C() throw();
デフォルトの C のコンストラクターは、型 int または char の例外をスローできます。 C のコピー・コンストラクターは、どのような種類の例外もスローできます。 C のデストラクターは、いかなる例外もスローできません。
関連参照