本の虫

著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。

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