空想犬猫記

※当日記では、犬も猫も空想も扱っておりません。(旧・エト記)

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)