C++の例外指定
C++では、関数に例外指定というものが記述できる。これはC++98からある機能で、throw(T1, T2, ...)という文法で、関数が外に投げる例外を指定する機能だ。
// C++98/03
void f() throw( int, double ) ;
もし、関数が例外指定に指定された以外の型を投げた場合、std::unexpectedが呼ばれる。
void f() throw( int )
{
throw 0 ; // OK
throw 0.0 ; // 実行時エラー: call std::unexpected()
}
例外指定は、関数から抜けだせる例外を指定する機能で、関数の外に例外を投げなければ問題がない。
void f() throw( int )
{
try {
throw 0.0 ; // OK
} catch( ... ) { } // OK、外に投げない
}
というのは、C++11以前の歴史の話だ。
現実に、この例外指定を真面目に実装するコンパイラーは少なかった。パースだけして無視するような有名なコンパイラーがはびこったのだ。結局、実装されないのであれば机上の空論でしかない。C++11では、これを動的例外指定と呼び、deprecated扱いしている。規格が現実に追いついたというわけだ。
ところで、この例外指定の文法は、本来規格が想定していなかった意味で使われ始めた。動的例外指定は、文法上、型を指定しないこともできる。
void f() throw( ) ;
ある主要なC++コンパイラーは、この記述を、関数は例外を外に投げないという意味にみなし、最適化のためのヒントに使い始めた。これは、本来規格設計時に意図された意味ではなかったが、最適化のヒントのため、広まった。世の中のC++コンパイラーは、動的例外指定を完全に実装しなかったが、これは実装するだけの価値があったのだ。
C++11では、無例外保証の指定機能だけに特化した、noexceptが追加された。noexceptを指定すると、関数は外に例外を投げないと指定したことになる。
void f() noexcept ;
もし外に例外を投げると、std::terminateが呼ばれる。
また、noexceptにはbool型のコンパイル時定数のオペランドを取る文法がある。式がtrueの時は無例外指定になり、falseの時は無例外指定にならないという意味を持つ。
void f() noexcept(true) ; // 例外を外に投げない
void g() noexcept(false) ; // 例外を外に投げる可能性がある
これにより、コンパイル時メタプログラミングにより、関数の無例外指定の有無を切り替えられるようになった。
無例外指定のない通常の関数宣言は、例外を外に投げる可能性がある関数である。throw()も、無例外指定とみなされる。
void f1() ;
void f2() throw() ; // 無例外指定
void f3() noexcept ; // 無例外指定
void f4() noexcept( true ) ; // 無例外指定
void f5() noexcept( false ) ;
また、C++11ではnoexcept式というものが追加されている。これは、オペランドの式が例外を投げるかどうかをboolで返す。オペランドの式は未評価式である。
void f() ;
void g() noexcept ;
int main()
{
noexcept( f() ) ; // false
noexcept( g() ) ; // true
}
ドワンゴ広告
この記事はドワンゴ勤務中に書いた。
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0