C++のif条件式の中での変数宣言

dynamic_castを利用する際に以下のように書く場面が出てくる.

Base* base = new DerivA();

if (DerivA* derivA = dynamic_cast<DerivA*>(base))
{
  ; // do something with derivA
}

if (DerivB* derivB = dynamic_cast<DerivB*>(base))
{
  ; // do something with derivB
}

この場合, 二度目のdynamic_castは失敗するため, if文の中身も実行されない.

しかし, 実際のところこれはこの宣言式の何を判断しているのか? この記法について書いている人は多かったのだがそれを説明している人は少なさそうだったので調べてみた. 規格書(?)*1を確認してみると

condition:
      expression
      attribute-specifier-seqopt decl-specifier-seq declarator = initializer-clause
      attribute-specifier-seqopt decl-specifier-seq declarator braced-init-list

となっており, 確かに変数宣言が許可されている(p.125). ちなみにだが, parenthesized initializerは許可されていない. DerivA* derivA(dynamic_cast(base))とは書けない.

では, 判定はどうなっているかというと, 皆様ご想像の通り,

The value of a condition that is an initialized declaration in a statement other than a switch statement is the value of the declared variable contextually converted to bool (Clause 4). If that conversion is ill-formed, the program is ill-formed.

switchで使う場合以外は, declarator(上で宣言された変数)をboolにキャストしてその値を使う, とある. キャストできない変数の宣言は許されない. というわけで, dynamic_castが成功した時だけ実行されるのは, dynamic_castが失敗したときにnullptrを返し, nullptrはbool値としてはfalseになるためである.

単にスコープの目的だけでここに変数宣言することは意味もないし危険である.

if (int x = 1)
{
  ; // play with 1
}

if (int x = 0)
{
  ; // play with 0
}

二つ目のif文の中身は実行されない. こんなん間違えないよ, という人もいるかもしれないが, この記法について調べたときには以下のような例がわんさと出てきた.

if (double x = some_function(y, z))
{
  ; // this works only if x is not zero.
}

これを意図的に使えないこともない*2が, はっきり言って僕は分かりにくいと思うのでポインタ以外では使わない方が良いのではないか.

*1:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

*2:独自のクラスでboolへのキャストを実装しておくとか