enum の最後で列挙子の数を定義することの是非

何の話?

C, C++ での列挙宣言 enum について、列挙子の数が欲しいことがよくあります。例えば各列挙子に対応する情報を取り出すためのテーブルを以下のように簡単な配列で定義して対応付けを行いたい場合などです。

int PriceOf(enum Item x)
{
  static int const price[NumItems] = {
    123, // Item_A の値段
    456, // Item_B の値段
    789, // Item_C の値段
  };
  return price[x];
}

この列挙子の数 NumItems を下記のように定義する手法があります。

enum Item
{
  Item_A,
  Item_B,
  Item_C,
  NumItems
};

このように NumItems を定義すると、このあと Item_D, Item_E, ... などが増えていっても NumItems が最後においてある限り NumItems は「列挙子の数」として正しい値であり続けます。わずらわしいメンテナンスが不要であり、便利です。

この手法は便利なのでよく見かけるのですが、僕はこの手法には見落とされがちなデメリットがあると考えています。

何が問題?

じゃぁどうすれば?

ここでは下記のような手法を提案します。(コメントも「手法」に含みます。)

enum Item
{
  Item_A,
  Item_B,
  Item_C
  // ※最後に追加するときは↓も更新してね。
};
int const NumItems = Item_C + 1;

ここで Item_C の後ろにカンマを置いていないのは意図的なもので、コメントを見ないで追加するような迂闊なプログラマにコンパイルエラーを見舞って注意を促すための策です。

それはちょっと・・・どうだろうか?

提案の手法では明らかに、最初に挙げた手法が解決してくれたはずの列挙子の数のメンテナンスが問題として復活しています。しかしどちらの手法でもそれぞれに問題があるのであれば、どちらにするかはそれらの問題の大小で考えるべきでしょう。

まず最初に挙げた手法の問題は、列挙型を利用する箇所の数に比例して大きくなります。次に提案の手法の問題(更新ミスを起こす可能性)は、列挙の最後に追加などの編集が加えられる回数に比例して大きくなります。僕の結論は、列挙型を利用する箇所の数は時間が経つにつれて発散していくのに対して列挙の最後が編集される回数は時間が経つにつれて収束していくので、提案の手法を基本として使うほうが問題は小さくなることが多いだろう、というものです。

理想を言えば、コンパイラから NumItems みたいな情報を取り出せるようになっててくれたらいいんですけどね。


Copyright © 2012 里田 和敏 (k_satoda@f2.dion.ne.jp)