読者です 読者をやめる 読者になる 読者になる

C++11においてenum class型が追加されたのは幸いだ

 先の記事でC++11前のC++の仕様に大して不満はない、と書いたのですが、一つ思い当たる不満がありました。
 それは、enum型で定義された値が、グローバル変数の様にアクセスできてしまうところで、アクセス出来るだけなら問題ないのですが、enum値の名前を工夫しないと名前解決で衝突が起きやすくなるという問題が有り、そのせいで値名にプリフィックスなどを付けるなどというカッコ悪いことをしたりしており、不満といえば不満でした。
 しかしC++11では、enum class内で定義されたenum class値は、enum class名が親スコープとなり、スコープ名を指定して明示的にアクセスしなければならなくなりました。これは良いです。旧enum型でも、enum型名をスコープとして明示的にアクセス出来ていましたが、そういう記述ができるというだけで、スコープ名無しでのアクセスも無制限だったために意味がありませんでした。
 enum classは下記のように使います。

enum class NewEnum
{
	Strong,
	Weak,
	Normal
};

enum OldEnum
{
	Strong,
	Weak,
	Normal
};

int _tmain(int argc, _TCHAR* argv[])
{
	NewEnum ne = NewEnum::Strong; //Correct
	ne = Strong; //Wrong
	OldEnum oe = Strong; //Correct

	return 0;
}

 上記サンプルコードをみてもらうとわかりますが、enum classとenumで同じ値名を使っていますが、スコープのないenum型と、スコープのあるenum class型で使い分ける事が出来ています。もちろん相互に代入は出来ません。
 小さいコードですが、値名を気にせず付け安くなった点で、enum classは強力だと言えます。
 また、enum class値は、即値としてオペランドや条件式に出来ない様です。キャストをすることでenum型同様に整数値を取り出すことは出来ます。以下の様なことをするとエラーになります。

enum class NewEnum
{
	Strong,
	Weak,
	Normal
};

enum OldEnum
{
	Strong,
	Weak,
	Normal
};

int _tmain(int argc, _TCHAR* argv[])
{
	int res = NewEnum::Normal - NewEnum::Strong; //Error
	cout
		<< (int)NewEnum::Strong << endl //Correct
		<< NewEnum::Weak << endl //Error
		<< NewEnum::Normal << endl //Error
		<< Strong << endl //Correct
		<< Weak << endl //Correct
		<< Normal << endl; //Correct

	NewEnum exp(NewEnum::Strong);
	if (NewEnum::Strong == exp) //Correct
		cout << "strong" << endl;
	if (NewEnum::Strong) //Error
		cout << "strong" << endl;

	getchar();
	return 0;
}

 enum型とenum class型との間でキャストは出来ますが、両方の値が同じでないと、コンパイルエラーこそ出ませんが、論理的におかしいことになることはあります。以下の様なケースがそれです。

enum class NewEnum
{
	Strong = 2,
	Weak = 3,
	Normal = 4
};

enum OldEnum
{
	Strong,
	Weak,
	Normal
};

int _tmain(int argc, _TCHAR* argv[])
{
	OldEnum en = (OldEnum)NewEnum::Strong; //Correct

	getchar();
	return 0;
}

 これらを見ると、やはりコーディングの安全性を高めるためには、enum class型を主に使って行ったほうが良いと思われます。