Item 10: Prefer scoped enums to unscoped enums.

Item 10: Prefer scoped enums to unscoped enums.

Effective Modern C++ Item 10 的学习和解读。

一般来说,定义在花括号中的对象,其作用域也在花括号内,但是 C++98 风格的枚举(enums)类型却不遵从这一规则:

enum Color { black, white, red }; // black, white, red are in same scope as Color
auto white = false;               // error! white already declared in this scope

上面这种将枚举成员暴露在其花括号作用域外面的方式,官方称为无作用域限制的枚举(unscoped enums)。C++11 提供不暴露枚举成员的枚举类型:有作用域限制的枚举(scoped enums)。

enum class Color { black, white, red }; // black, white, red are scoped to Color
auto white = false;                     // fine, no other

减少名称污染

从上面的两个例子,可以看到 scoped enums 可以减少因作用域限制导致的名称污染,这是 scoped enums 的第一个优点。

有强类型

scoped enums 的第二个优点是其成员有强类型,不会发生隐式类型转换。看个例子:

enum Color { black, white, red }; // unscoped enum
std::vector<std::size_t>  primeFactors(std::size_t x); 
Color c = red;if (c < 14.5) {       // compare Color to double (!)
  auto factors = primeFactors(c); // compute prime factors of a Color (!) }

scoped enums 则不会发生隐式类型转换,上面的例子的 Color 修改成 scoped enums 方式,则会产生编译报错:

enum class Color { black, white, red }; // enum is now scoped
Color c = Color::red;if (c < 14.5) {  // error! can't compare Color and double
auto factors = primeFactors(c);  // error! can't pass Color to function expecting std::size_t}

如果你一定要将 Color 转换为其他类型,可以使用显示类型转换:

if (static_cast<double>(c) < 14.5) { // odd code, but it's valid
auto factors = primeFactors(static_cast<std::size_t>(c)); // suspect, but it compiles}

前向声明

scoped enums 的第三个优点是其可以前向申明,也即枚举名可以在没有指定其成员前进行申明。

enum Color; // error!
enum class Color; // fine

其实,说 unscoped enums 不支持前向申明是不严谨的,其实在 C++11 中,它也可以前向申明,只不过不支持上面例子这样的前向申明,要想让它可以前向申明,还需要进行一点修改而已。所谓的前向申明,其本质就是告诉编译器目标是什么类型。

这里有一个事实,那就是unscoped enums 类型其实不是一个 enums,它的实际类型是编译器进行选择决定的。 scoped enums 之所以能够前向申明,是因为它的默认潜在类型(underlying type)是 int

enum Color { black, white, red };

对于上面代码,编译可以将 Color 类型选择成 char 就足够了。

enum Status { good = 0,
              failed = 1,
              incomplete = 100,
              corrupt = 200,
              indeterminate = 0xFFFFFFFF
};

但是,对于上面这段代码,编译器可能需要将 Status 类型选择成 int

总之,对于有作用域的枚举体的潜在类型是已知的,对于没有作用域的枚举体,你可以指定它。

对于 scoped enums ,默认的潜在类型为 int,当然你也可以进行更改:

enum class Status; // underlying type is int

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

对于 unscoped enums,指定了潜在类型,则可以前向申明:

enum Color: std::uint8_t; // fwd decl for unscoped enum; underlying type is std::uint8_t

unscoped enums 的优势

上面介绍了 scoped enumsunscoped enums 的几个优点。其实,unscoped enums 也有它自己的优势。看下面的例子:

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

虽然注释说明了元组每个部分代表的含义,但是,当你遇到下面的代码时候,你可能还是记不清楚元组的第一个元素代表的是啥:

UserInfo uInfo; // object of tuple typeauto val = std::get<1>(uInfo); // get value of field 1

如果使用 unscoped enums 修改上面的代码,可以避免这个问题:

enum UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo uInfo; // as beforeauto val = std::get<uiEmail>(uInfo); // ah, get value of email field

这里,UserInfoFieldsstd::get() 要求的 std::size_t 的隐式类型转换。要是使用 scoped enums 则代码要冗余的多:

enum class UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo uInfo; // as beforeauto val =
std::get<static_cast<std::size_t>(UserInfoFields::uiEmail)>(uInfo);

总结下:

  • C++98 风格的 enumunscoped enum
  • scoped enums 的枚举成员仅仅对枚举体内部可见。只能通过类型转换( cast )转换为其他类型。
  • scopded enumsunscoped enum 都支持指定潜在类型。scoped enum 默认潜在类型是 intunscoped enum 没有默认的潜在类型。
  • scoped enum 总是可以前置声明的。unscoped enum 只有当指定潜在类型时才可以前置声明。
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值