enum にしょんぼり

id:faith_and_braveC++0xenum は基底型に汎整数型しか指定できないよ、と指摘されたので修正。ちょっと妄想の翼が暴走してしまったようで。ちなみに「はげます」 & 話術士禁止 play とか実はけっこうツライです、であってるかな ?

ちょっと enum まわりいじってたんだけどなんかダメ感漂っている。とりあえずある集合の中の要素のひとつしか値として持てないような型を定義したい ( 種類が有限個かつ列挙するのに現実的な数しか存在しない何かをひとくくりに型として扱いたい ) という場合に enum を使うのが自然かなと思って使ってたんだけどちょっと欲を出すとどうにも。

まずそもそものハナシとして int 型へ昇格可能というのはなんというか勘弁願いたい。 switch 文の制約からくる要求なのかな。順序が必要な集合を扱う場合はまぁわからなくはないんだけど ( でも order という property を他に持つことで表現すべきかな ) 、列挙型に対して四則演算ができてしまう ( できなきゃいけない場面を思いつかないせいもあるけど ) という時点でもうわけがわからない。逆の操作 ( int 型から特定の列挙型へ落とし込む ) ができないというのは必要性もないしできてしまっては困るしということでまぁわかるんだけど。

この点の解決策として type safe enum という手法がある ( http://www.geocities.jp/bleis_tift/cpp/typesafeenum.html ) んだけど ( idiom ってほど短くない ) これは data を class 定義時に埋め込まないといけないので他の型安全な列挙を定義したい場合も似たような code を書かなくちゃいけない。ちなみに試しにこれをそのままコピペして compile してみたんだけど VC9 でも g++4.4 でも通らない。なんか link 時に怒られる。

でまぁある列挙子の名前というかその要素がどういったものかを表わす文字列のようなものを持てないというのもイケてないというか何というか。そういうことがしたい場合、例えば関数をひとつ用意してその中で switch 文なりで判断して要素毎の名前を返すような方法が考えられるんだけど data 構造がふたつにわかれてしまっているので readability も maintainability も落ちてしまう。まぁ要は列挙型を cout なんかに流し込むと int 型に昇格された値が表示されてしまって人間にやさしくないので同時に表示名みたいなものも持たせられたらいいねとかそういうハナシなんだけど。

いやまぁ例えばその表示名が一意に定まらない場合 ( その集合に対して解釈や表現が複数ある場合 ) 表示名は表示する program ごとに定義していけばいいと思うんだけど、例えば名前が明確に決まっているもの ( 例えば法律や国際標準で決まってるとか学名とか ) に関してはもう enum 宣言時に同時に指定できたらなーとか。

C++0x ではここらへんがけっこう改善されるみたいで ( http://github.com/januswel/Cxx0xISja/blob/master/7.2%20Enumeration%20declarations%20%5Bdcl.enum%5D.txt ) "enum struct" や "enum class" という keyword を使うことで int 型への昇格を抑制したり基底型を指定することで列挙子の型を指定できるらしい。どういうことかというと、

// this program is wrong and ill-formed
#include <string>

enum struct Fruit : std::string {
    APPLE = "apple",
    ORANGE = "orange",
    BANANA = "banana",
    PINEAPPLE = "pineapple"
};

とやると int 型へ昇格しない、値として文字列を持つ列挙を定義できる。こうすると ( 多分 ) switch による分岐は不可能になるものの cout へ流し込むと名前を表示してくれる列挙が簡単に実現できる ( はず ) 。 switch も使いたいという場合は…、基底型を指定しない従来の enum を使うしかないのかな。

指定できる基底型は汎整数型に限定されるらしい。ということはつまり新しく定義する列挙の size と列挙子の数を明示的に指定することができるようになる、という解釈であってるかな。 char を基底型に指定すると 1 byte の size を持つ 256 個の列挙子を持てる型を定義できるわけだ。あとは…、 bool を基底型に指定して true と false に別名をつけるっていうのは普通に便利そう。以下の例はやる価値があるかわからないけど可読性は上がると思う。

enum struct yesno_t : bool { YES = true, NO = false };

でもやっぱり表示名を持つことは無理なわけか。うーん enum struct とその列挙子の表示名を返す関数を member に持つ class を作りなさいということかな。