EMCPP条款10:优先选用限定作用域的枚举型别,而非不限作用域的枚举型别

YiQi 管理员
1
2
3
4
5
enum class Color { black, white, red }; // black, white, red are scoped to Color
auto white = false; // fine, no other "white" in scope
Color c = white; // error! no enumerator named "white" is in this scope
Color c = Color::white; // fine
auto c = Color::white; // also fine (and in accord with Item 5's advice)

限定作用域的枚举型别带来的名字空间污染降低,已经是“应该优先选择它,而不是不限范围的枚举型别”的足够理由。但是限定作用域的枚举型别还有第二个压倒性优势:它的枚举量是更强型别的 (strongly typed) 。不限范围的枚举型别中的枚举量可以隐式转换到整数型别(并能够从此处进一步转换到浮点型别)。

默认地,限定作用域的枚举型别的底层型别是 int, 如果默认型别不合你意,你可以推翻它:

1
2
enum class Status: std::uint32_t; 
// underlying type for Status is std::uint32_t (from <cstdint>)

底层型别指定同样也可以在枚举型别定义中进行:

1
2
3
4
5
6
7
8
enum class Status: std::uint32_t { 
good = 0,
failed = 1,
incomplete = 100,
corrupt = 200,
audited = 500,
indeterminate = 0xFFFFFFFF
};

在至少一种情况下,不限范围的枚举型别还是有用的:那就是当需要引用 C++11 中的 std::tuple 型别的各个域时。例如,假设我们要为一个社交网站准备一个元组来持有名字、电子邮件和声望值:

1
2
3
4
using UserInfo = // type alias; see Item 9
std::tuple< std::string, // name
std::string, // email
std::size_t > ; // reputation

作为程序员,你要跟进的事项很多。你真的应该记住 1 对应用户的电子邮件吗?我并不这么认为。而采用一个不限范围的枚举型别和域序数关联就可以消解这种记忆需要:

1
2
3
4
enum UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo uInfo; // as before
...
auto val = std::get<uiEmail>(uInfo); // ah, get value of email field

而采用限定作用域的枚举型别版本,可以写个函数:

1
2
3
4
template<typename E>
constexpr typename std::underlying_type<E>::type toUType(E enumerator) noexcept {
return static_cast<typename std::underlying_type<E>::type>(enumerator);
}

在 C++14 中可以写成

1
2
3
4
template<typename E> // C++14
constexpr std::underlying_type_t<E> toUType(E enumerator) noexcept {
return static_cast<std::underlying_type_t<E>>(enumerator);
}

要点速记

  • C++98 风格的枚举型别,现在称为不限范围的枚举型别。
  • 限定作用域的枚举型别仅在枚举型别内可见。它们只能通过强制型别转换以转换至其他型别。
  • 限定作用域的枚举型别和不限范围的枚举型别都支持底层型别指定。限定作用成的枚举型别的默认底层型别是 int, 而不限范围的枚举型别没有默认底层型别。
  • 限定作用域的枚举型别总是可以进行前置声明,而不限范围的枚举型别却只有在指定了默认底层型别的前提下才可以进行前置声明。
此页目录
EMCPP条款10:优先选用限定作用域的枚举型别,而非不限作用域的枚举型别