传统枚举类型的缺点
- 非强类型作用域
由于General都是全局的名字,因此编译会报错。enum Type { General, Light, Medium, Heavy }; enum Category { General, Pistol, MachineGun, Cannon };
- 允许隐式转换为整型
#include <iostream> using namespace std; enum Type { General, Light, Medium, Heavy }; // 无法编译通过,重复定义了General //enum Category { General, Pistol, MachineGun, Cannon }; enum Category { Pistol, MachineGun, Cannon }; struct Killer { Killer(Type t, Category c) : type(t), category(c){} Type type; Category category; }; int main() { Killer cool(General, MachineGun); // ... // ...其它很多代码... // ... if (cool.type >= Pistol) cout << "It is not a pistol" << endl; // ... cout << is_pod<Type>::value << endl; // 1 cout << is_pod<Category>::value << endl; // 1 return 0;
- 占用存储空间
#include <iostream> using namespace std; class Type { public: enum type { general, light, medium, heavy }; type val; public: Type(type t): val(t){} bool operator >= (const Type & t) { return val >= t.val; } static const Type General, Light, Medium, Heavy; }; const Type Type::General(Type::general); const Type Type::Light(Type::light); const Type Type::Medium(Type::medium); const Type Type::Heavy(Type::heavy); class Category { public: enum category { pistol, machineGun, cannon }; category val; public: Category(category c): val(c) {} bool operator >= (const Category & c) { return val >= c.val; } static const Category Pistol, MachineGun, Cannon; }; const Category Category::Pistol(Category::pistol); const Category Category::MachineGun(Category::machineGun); const Category Category::Cannon(Category::cannon); struct Killer { Killer(Type t, Category c) : type(t), category(c){} Type type; Category category; }; int main() { // 使用类型包装后的enum Killer notCool(Type::General, Category::MachineGun); // ... // ...其它很多代码... // ... if (notCool.type >= Type::General) // 可以通过编译 cout << "It is not general" << endl; #if 0 if (notCool.type >= Category::Pistol) // 该句无法编译通过 cout << "It is not a pistol" << endl; #endif // ... cout << is_pod<Type>::value << endl; // 0 cout << is_pod<Category>::value << endl; // 0 return 0; }
- 符号性不确定:标准规定,C++枚举类型所基于的“基础类型”是由编译器来具体指定实现的,这会导致枚举类型成员的基本类型的不确定性问题(尤其是符号性)
#include <iostream> using namespace std; enum C { C1 = 1, C2 = 2}; enum D { D1 = 1, D2 = 2, Dbig = 0xFFFFFFF0U }; enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFFFFLL}; int main() { cout << sizeof(C1) << endl; // 4 cout << Dbig << endl; // 编译器输出不同,GCC:4294967280, Visual C++输出-16 cout << sizeof(D1) << endl; // 4 cout << sizeof(Dbig) << endl; // 4 cout << Ebig << endl; // 68719476735 cout << sizeof(E1) << endl; // 8 return 0; }
C++11引入的强类型枚举(strong-typed enum)
enum class Type { General, Light, Medium, Heavy };
优势:
-
强作用域,强类型枚举成员的名称不会被输出到其父作用域空间;
-
转换限制,强类型枚举成员的值不可以与整型隐式地相互转换,但可以显示转换为其他类型;
-
可以指定底层类型。强类型枚举默认的底层类型为int,但也可以显示地指定底层类型,具体方法为在枚举名称后面上“:type”,其中type可以是除wchar_t以外的任何整型。比如:
enum class Type : char { General, Light, Medium, Heavy };
-
Type同时是POD类型,几乎不带来任何额外的开销;
匿名的强类型枚举
enum class { General, Light, Medium, Heavy } weapon;
int main()
{
weapon = General; // 无法编译通过
bool b = (weapon == weapon::General); // 无法编译通过
return 0;
}
我们声明了一个匿名的enum class实例weapon,却无法对其设置值或者比较其值,事实上,使用enum class的时候,应该总是为其提供一个名字。但可以通过decltype来获得匿名强类型枚举的类型并且进行使用(但这样做没什么太大的意义)。