[迷信] sizeof は定数式

C/C++ の sizeof は、オペランドの型情報を元に、オペランドの評価結果のサイズをバイト数で返します。評価結果の値がどうなるかは関係ありません。C++ の型には、静的な型と動的な型(ポインタまたは参照が実際に指している多相オブジェクトの型)がありますが、sizeof 演算子は必ず静的な型のサイズを返します。

1998 年に C++ の標準規格が出来た頃までは、sizeof は必ず定数式になると考えて問題ありませんでした。しかし、C99 ではそうはいかないのです。C99 には可変長配列があります。可変長配列は次のように使います。

void func(int n)
{
  int array[n];
  ...
}

この func 関数の中で sizeof array とすればどうなるでしょうか? 意味的に考えて、この sizeof 演算子は sizeof(int) * n に評価されることでしょう。しかし、n は定数式ではなく、func 関数が実際に呼び出されるまで決定できません。

C++ に限れば、現時点では sizeof は必ず定数式になると考えて問題ありませんが、次期規格ではおそらく C99 の仕様が取り入れられるでしょうから、将来的には常に定数式になるとは限らないと考えた方がよさそうです。

実用上の問題としては、C の場合、switch 文のラベルに sizeof を使った式を記述できないことぐらいしか考えられません。C90 では、集成体(配列と構造体)の初期化子には定数式しか含めることができませんでしたが、C99 では、この制約は静的記憶域期間を持つ場合に限られるため、実質的な問題はありません。あと、関数の中で列挙体を定義した場合に、定数式でなければ列挙子の値に使うことができないという問題がありますが、実際に遭遇することはまずないでしょう。

一方、C++ の場合には深刻な問題になるかもしれません。特に、テンプレートメタプログラミングでは、静的な型の判別に sizeof を使うことが多いため、思わぬところでコンパイルエラーが出るかもしれません。もっとも、それはエラーで当然なのですが、設計段階で気付かないと痛い目を見ることもきっとあるでしょう。ちなみに、GCC などの C99 に対応した処理系では、現時点でも C++ の sizeof が常に定数式になるとは限りません。

ところで、GCC のように typeof 演算子が使える処理系では、可変長配列を使った場合には typeof もある意味動的に解決されることになります。次のようなコードで簡単にエラーになってしまうので要注意です。おそらく、こちらは sizeof より罠にはまる確率が高そうです。

template <typename T>
class A
{
};

int n;

int main()
{
  int array[n];
  A<typeof(array)> a;
  return 0;
}