大家枚举类型是不是都是这样实现的,这种枚举被称做弱枚举类型。
#includeenum Color{ RED, GREEN, BLUE, }; void fun() { int color = RED; ::std::cout << color << ::std::endl; }
在弱枚举中,枚举类型是不限定作用域的(unscoped enumeration),可以不加命名空间随意使用,但不限定作用域的用法总是充满危险的。例如:
enum ColorB {
RED.
};
这样定义新的枚举ColorB,与其他弱枚举(Color)里面的RED与冲突。
不仅如此,弱枚举类型默认可以被隐式转换为int类型,还存在这枚举类型与基本类型的比较。
void fun() {
int color = RED;
int color2 = 1;
if (color < color2) ::std::cout << color << " < " << color2 << ::std::endl;
}
//0 < 1
这种状况很容易对整型和浮点型的隐式转换,造成乱用(本来你不相进行转换或比较)。或者是这样传递参数,有2个类A、B,都有一种id属性,当通过id获取A或者B对象的时候,会出现问题,例如:
class A {
public:
uint64_t id() const;
};
class B {
public:
uint64_t id() const;
};
::std::shared_ptr get_a_by_id(uint64_t id);
::std::shared_ptr get_b_by_id(uint64_t idA,uint64_t idB);
当获取get_b_by_id的时候,把idB误当做idA传入,在运行的过程中,才会发现这个问题,当项目比较大的时候,debug会很复杂,不好定位问题。
struct UID {
uint64_t id;
constexpr explicit UID(uint64_t id) noexcept : id(id) {
}
};
class A {
public:
uint64_t id() const;
};
class B {
public:
uint64_t id() const;
};
::std::shared_ptr get_a_by_id(UID id);
定义一个UID的类,维护类型的隐式转换,传入数据的时候,指定类型。但是通常在函数开头判断id是否不为空,需要重载UID类的bool判断
struct UID {
uint64_t id;
constexpr explicit UID(uint64_t id) noexcept : id(id) {
}
constexpr explicit operator bool() const noexcept {
return id != 0;
}
};
::std::shared_ptr get_a_by_id(UID id) {
if (!id) {
return nullptr;
}
// ...
}
需要打印log的时候,还需要重载<<运算符。
struct UID {
uint64_t id;
constexpr explicit UID(uint64_t id) noexcept : id(id) {
}
constexpr explicit operator bool() const noexcept {
return id != 0;
}
friend ::std::ostream& operator<<(::std::ostream& os, UID id) {
return os << id.id;
}
};
class A {
public:
uint64_t id() const;
};
class B {
public:
uint64_t id() const;
};
::std::shared_ptr get_a_by_id(UID id) {
if (!id) {
return nullptr;
}
// ...
::std::cout << "get_a_by_id: " << id << ::std::endl;
}
定义一个强枚举,类型安全,不会因为隐式转换而造成歧义。
enum UID : uint64_t;
class A {
public:
uint64_t id() const;
};
class B {
public:
uint64_t id() const;
};
::std::shared_ptr get_a_by_id(UID id) {
if (!id) {
return nullptr;
}
// ...
::std::cout << "get_a_by_id: " << id << ::std::endl;
}
还可以利用static_cast(1)进行类型转换,int --> UID(强枚举),更加的清晰,增加可读性,减少歧义。
指定enum类型的好处是:
可以控制不同实验环境中使用的类型,我们可以确保在一种实现环境中编译通过的程序所产生的代码与其他实现环境中产生的代码一致。
——《C++ Primer》



