return throw 問題
今日ハマったC++の落とし穴。次のコードの出力結果を予想してください。
#include <iostream> #include <exception> using namespace std; static void assertZero(int i) { if (i == 0) return // // I'm sure it is not a zero!! // throw exception("Not a zero"); } int main() { try { assertZero(0); cout << "0 is zero.\n"; } catch (...) { cerr << "0 is not zero.\n"; } try { assertZero(1); cout << "1 is zero.\n"; } catch (...) { cerr << "1 is not zero.\n"; } return 0; }
これは
0 is zero. 1 is not zero.
となって欲しいところだけど、実際には以下のようになる。
0 is not zero. 1 is zero.
これ見て紅茶を噴きかけたのだが、こうなった原因は、assertZero 関数の return 直後にセミコロンをつけ忘れたことにある。C++の仕様によれば return の後には式を書くことができて、throw は void 型の式だったんですな。つまり返り値が void 型の関数において return throw exception("..."); と書くのは仕様上セーフであるらしい。結果として assertZero 関数は
static void assertZero(int i) { if (i == 0) return throw exception("Not a zero"); }
と解釈され、めでたく全く意図とは正反対の挙動をするというわけでした。Go や TypeScript など、セミコロンの要らないモダンな言語に侵食されると、この手のミスが増えることが予想されます。これを機に中括弧を省略しない宗派に切り替えることも検討中。
(再現環境:Visual Studio 2017)