C语言风格枚举类型 enum
这是一个enum
类型
enum LogType {OK,Info,Error
};
可以像这样指定enum
的基础类型(默认类型是int
),用英文来说就是underlying type
enum LogType : unsigned int {OK,Info,Error
};
继续升级,指定每个枚举元素具体的值
enum LogType : unsigned int {OK = 0x00,Info = 0x01,Error = 0x02
};
经典位掩码,即Bitmask
写法
enum WindowFlags : unsigned int {None = 0x0000,NoMove = 0x0001,NoResize = 0x0002,NoSavedSettings = 0x0004
};
auto MySettings = NoMove | NoResize;
但是enum
本质上其实是无范围枚举,即unscoped enumeration
,很容易就会发现命名冲突问题
enum LogType {OK,Info,Error
}; // OKenum StatusCode {OK,Forbidden
}; // OK?auto res = LogType::OK; // Compiler doesn't know which OK to use even using :: to specify
想要解决这个问题并不难,我们可以外面套一层struct
或者class
struct LogType {enum {OK,Info,Error};
};class StatusCode {enum {OK,Forbidden};
};auto res = LogType::OK; // OK!
但是这样似乎不太优雅,尤其是我们想将enum
作为函数返回值的时候
struct StatusCode {enum {OK,Forbidden};
};StatusCode foobar() {return StatusCode::OK; // Hey, don't do this!
};
C++风格枚举类型 enum class
为了解决以上种种问题,C++引入了enum class
,这是一种范围枚举,即scoped enumeration
,不存在命名冲突
用法和enum
相似,无非就是在后面添加了一个struct
或者class
(等效)
enum struct LogType {OK,Info,Error
};enum class StatusCode {OK,Forbidden
};
我们也可以像enum
一样把enum class
类型作为函数返回值(可以把它看作是一个类)
enum class StatusCode {OK,Forbidden
};StatusCode foobar() {return StatusCode::OK;
};
值得注意的是,enum class
并不支持隐式类型转换,即implicit type conversion
因此,enum class
也并不能像enum
那样直接进行位运算
enum class WindowFlags : unsigned int {None = 0x0000,NoMove = 0x0001,NoResize = 0x0002,NoSavedSettings = 0x0004
};WindowFlags foobar() {return WindowFlags::None;
};
if (!foobar())exit(EXIT_FAILURE); // g++: expression must have bool type (or be convertible to bool)
auto MySettings = NoMove | NoResize; // g++: no operator "|" matches these operands
auto MySettings = static_cast<WindowFlags> (static_cast<unsigned int> (WindowFlags::NoMove) | static_cast<unsigned int> (WindowFlags::NoResize)); // OK
显示类型转换,即explicit type conversion
似乎过于臃肿,但是我们可以重载一下enum class
相关的operator
,以下模板放在我们的enum class
的命名空间内即可(具体细节此处不赘述,请自行查阅std::enable_if_t
, std::is_enum_v
)
template <typename enum_type, typename cast_type = std::size_t>
constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator& (enum_type x, enum_type y) {return static_cast<enum_type>(static_cast<cast_type> (x) & static_cast<cast_type> (y));
}template <typename enum_type, typename cast_type = std::size_t>
constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator| (enum_type x, enum_type y) {return static_cast<enum_type>(static_cast<cast_type> (x) | static_cast<cast_type> (y));
}template <typename enum_type, typename cast_type = std::size_t>
constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator^ (enum_type x, enum_type y) {return static_cast<enum_type>(static_cast<cast_type> (x) ^ static_cast<cast_type> (y));
}template <typename enum_type, typename cast_type = std::size_t>
constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator~ (enum_type x) {return static_cast <enum_type> (~static_cast<cast_type> (x));
}template <typename enum_type, typename cast_type = std::size_t>
constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator&= (enum_type &x, enum_type y) {x = x & y;return x;
}template <typename enum_type, typename cast_type = std::size_t>
constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator|= (enum_type &x, enum_type y) {x = x | y;return x;
}template <typename enum_type, typename cast_type = std::size_t>
constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator^= (enum_type &x, enum_type y) {x = x ^ y;return x;
}
enum class
严格意义上来说并不是一个常规意义上的class
,所以无法通过重载自动转换为bool
类型,但是我们可以手写一个any
函数来判断位掩码
template <typename enum_type, typename cast_type = std::size_t>
constexpr std::enable_if_t<std::is_enum_v<enum_type>, bool> any(enum_type x) {return static_cast<cast_type> (x) == 0;
};
简单样例(已载入上述模板)
enum class WindowFlags : unsigned int {None = 0x0000,NoMove = 0x0001,NoResize = 0x0002,NoSavedSettings = 0x0004
};auto MySettings = WindowFlags::NoMove | WindowFlags::NoResize;if (any(MySettings)) { // To check if any flags are set/* do something */
}
参考链接
cppreference.com