C++11 的枚举是“域化的” (scoped enum),相比 C++98 枚举的“非域化” (unscoped enum),具有如下优点:

1  命名空间污染 

声明在 {} 内的变量,其可见性限制在 {} 作用域内,但是非域化枚举 (unscoped enum) 却是例外

  1. enum Color { yellow, green, blue}; // yellow, green, blue are in same scope as Color
  2.  
  3. auto yellow = false; // error! yellow already declared in this scope
  4.  
  5. Color c = yellow; // fine

C++11 枚举的关键字为 enum class,可视为一个 "class",能防止“命名空间污染”

  1. enum class Color { yellow, green, blue}; // yellow, green, blue are scoped to Color
  2.  
  3. auto yellow = false; // fine, no other "yellow" in scope
  4.  
  5. Color c = yellow; // error! no enumerator named "yellow" is in this scope
  6.  
  7. Color c = Color::yellow; // fine
  8.  
  9. auto c = Color::yellow; // also fine

2  强类型枚举

非域化的枚举成员,可以隐式的转换为广义整型 (integral types),如下所示:

  1. enum Color { yellow, green, blue}; // unscoped enum
  2. std::vector<std::size_t> primeFactors(std::size_t x); // func. returning prime factors of x
  3.  
  4. Color c = blue;
  5.  
  6. if (c < 14.5) // compare Color to double (!)
  7. {
  8. auto factors = primeFactors(c); // compute prime factors of a Color (!)

  9. }

域化的枚举成员,却不可以隐式的转换为广义整型

  1. enum class Color { yellow, green, blue }; // enum is now scoped
  2.  
  3. Color c = Color::blue; // as before, but with scope qualifier
  4.  
  5. if (c < 14.5) // error! can't compare Color and double
  6. {
  7. auto factors = primeFactors(c); // error! can't pass Color to function expecting std::size_t

  8. }

正确的方式是使用 C++ 的类型转换符 (cast)

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

  4. }

3  前置声明

域化枚举支持前置声明 (forward-declared),也即可以不用初始化枚举成员而声明一个枚举类型

  1. enum class Color;

3.1 新增枚举成员

非域化枚举(unscoped enum) 在声明时,编译器会选择占用内存最小的一种潜在类型 (underlying types),来代表每一个枚举成员

  1. enum Color { black, white, red }; // compiler may choose char type

下面的例子中,编译器可能会选择更大的能够包含 0 ~ 0xFFFFFFFF 范围的潜在类型

  1. enum Status {
  2. good = ,
  3. failed = ,
  4. incomplete = ,
  5. corrupt = ,
  6. indeterminate = 0xFFFFFFFF
  7. };

非前置声明的缺点在于,当新增一个枚举成员时 (如下例的 audited ),整个系统将会被重新编译一遍,即使只有一个很简单的函数使用了新的枚举成员 (audited)

  1. enum Status {
  2. good = ,
  3. failed = ,
  4. incomplete = ,
  5. corrupt = ,
  6. audited = ,
  7. indeterminate = 0xFFFFFFFF
  8. };

而使用前置声明,当新增枚举成员时,包含这些声明的头文件并不需要重新编译,源文件则根据新枚举成员的使用情况来决定是否重新编译

如下例,Status 中新增枚举成员 audited,如果函数 continuteProcesing 没有使用 audited,则函数 continuteProcesing 的实现并不需要重新编译

  1. enum class Status; // forward declaration
  2. void continueProcessing(Status s); // use of fwd-declared enum

3.2 潜在类型

域化枚举的潜在类型 (underlying type),缺省为 int 型,当然也可以自行定义潜在类型。不管哪种方式,编译器都会预先知道枚举成员的大小

  1. enum class Status; // underlying type is int
  2.  
  3. enum class Status: std::uint32_t; // underlying type for Status is std::uint32_t (from <cstdint>)
  4.  
  5. enum class Status: std::uint32_t // specify underlying type on enum's definition
    {
  6. good = ,
  7. failed = ,
  8. incomplete = ,
  9. corrupt = ,
  10. audited = ,
  11. indeterminate = 0xFFFFFFFF
  12. };

4  std::tuple

一般而言,使用 C++11 域化枚举 (scoped enum) 是比较好的选择,但 C++98 非域化枚举 (unscoped enum) 也并非一无是处

4.1  非域化枚举

当涉及到 std::tuple 时,使用 C++98 非域化枚举反而会有优势,如下例所示

假定一个社交网站中,每一位用户,都使用一种模板类型 - 元组 (tuple) 来包含名字、邮箱、声望值 (name, email, reputation value)

  1. using UserInfo = std::tuple<std::string, std::string, std::size_t> ; // type alias

当以下代码在另一个不同的源文件里时 (source file),很有可能忘了元组 (tuple) 的第一个成员到底是名字还是邮箱 (name or email)

  1. UserInfo uInfo; // object of tuple type
  2. ...
  3.  
  4. auto val = std::get<>(uInfo); // get value of field 1

但是,使用非域化枚举 (unscoped enum),可以不用担心忘记了元组内的成员顺序

  1. enum UserInfoFields { uiName, uiEmail, uiReputation };
  2. UserInfo uInfo; // as before

  3. auto val = std::get<uiEmail>(uInfo); // get value of email field

4.2  域化枚举 

上例中,假如使用域化枚举 (scoped enum),则会用到类型转换,看起来比较繁琐

  1. enum class UserInfoFields { uiName, uiEmail, uiReputation };
  2. UserInfo uInfo; // as before

  3. auto val = std::get<static_cast<std::size_t>(UserInfoFields::uiEmail)>(uInfo);

可以使用一个模板函数,将枚举成员 UserInfoFields::uiEmail 和 std::size_t 类型联系起来

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

这样,便可以稍微缩减了代码的复杂度。但是相比于非域化枚举,看起来还是有些繁琐

  1. auto val = std::get<toUType(UserInfoFields::uiEmail)>(uInfo);

小结:

1)  C++98 枚举类型是“非域化的”;而C++11 枚举类型是“域化的”,枚举成员只在域内可见

2)  域化枚举的缺省潜在类型 (underlying type) 是 int 型,而非域化枚举没有缺省潜在类型

3)  域化枚举一般总是前置声明,而非域化枚举只有在指定了潜在类型时才可以是前置声明

参考资料:

<Effective Modern C++> Item 10

C++11 之 scoped enum的更多相关文章

  1. item 10: 比起unscoped enum更偏爱scoped enum

    本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 一般情况下,在花括号中声明一个name(包括变量名,函数名),这个 ...

  2. Java-Runoob-高级教程-实例-方法:11. Java 实例 – enum 和 switch 语句使用

    ylbtech-Java-Runoob-高级教程-实例-方法:11. Java 实例 – enum 和 switch 语句使用 1.返回顶部 1. Java 实例 - enum 和 switch 语句 ...

  3. c++11 强类型枚举 enum class

    在标准C++中,枚举类型不是类型安全的.枚举类型被视为整数,这使得两种不同的枚举类型之间可以进行比较.C++03 唯一提供的安全机制是一个整数或一个枚举型值不能隐式转换到另一个枚举别型. 此外,枚举所 ...

  4. C++11:强类型枚举(enum)

    // C++11之前的enum类型是继承C的,不温不火: // C++11对enum动刀了,加强了类型检查,推出强类型enum类型,眼前一亮 // 使用过QT 的都知道,早就应该这么做了,用的非常爽! ...

  5. enum 与 enum class

    c++11中引入了新的枚举类型---->强制枚举类型 // unscoped enum: enum [identifier] [: type] {enum-list};  // scoped e ...

  6. c语言中enum类型的用法

    11.10 枚举类型 在实际问题中,有些变量的取值被限定在一个有限的范围内.例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等等.如果把这些量说明为整型,字符型或其它类型显然是不妥当的 ...

  7. C++11中的Lambda表达式

    原文地址:C++中的Lambda表达式 作者:果冻想 一直都在提醒自己,我是搞C++的:但是当C++11出来这么长时间了,我却没有跟着队伍走,发现很对不起自己的身份,也还好,发现自己也有段时间没有写C ...

  8. enum 在c中的使用

    假设一个变量你须要几种可能存在的值,那么就能够被定义成为枚举类型.之所以叫枚举就是说将变量或者叫对象可能存在的情况也能够说是可能的值一一例举出来.  举个样例来说明一吧,为了让大家更明确一点,比方一个 ...

  9. C++11学习

    转自: https://www.cnblogs.com/llguanli/p/8732481.html Boost教程: http://zh.highscore.de/cpp/boost/ 本章目的: ...

随机推荐

  1. wydomain

    目标系统信息收集组件,完全模块化,脚本均可拆可并.可合可分的使用! 运行流程 利用FOFA插件获取兄弟域名,并透视获取到的子域名相关二级域名.IP信息 检查域名和兄弟域名是否存在域传送漏洞,存在就遍历 ...

  2. linux heartbeat v2/v3 的一点资料

    http://linux-ha.org http://linux-ha.org/wiki/Pacemaker Heartbeat2 http://blog.taggesell.de/index.php ...

  3. iOS 7 与 Xamarin - MultiPeer Connectivity(转载)

    随着时代的改变,移动设备在生活工作都开始取代原有的pc.设备间的数据交互就成为了必备可少的功能.比较成熟的产品有NFC.这个啪啪的操作很流行,例如分享图片,分享文件等 .但是在iOS设备中还没有NFC ...

  4. Xcode8以及iOS10问题总结!

    Xcode8的问题 注释功能问题解决 打开终端,命令运行: sudo /usr/libexec/xpccachectl 重启电脑 在xib和stroyboard出现大量警告,需要重新适配,字体所占用宽 ...

  5. iOS 内存问题

    malloc: *** error for object 0x15f8a3558: incorrect checksum for freed object - object was probably ...

  6. TextView 字数限制

    给大家推荐一个  无bug的 字数限制   http://blog.csdn.net/u012460084/article/details/50377928

  7. linux下安装opcache扩展

    linux下安装opcache扩展   参考:http://www.php.net/manual/zh/opcache.installation.php 1 2 3 4 5 6 7 wget http ...

  8. Java中用内存映射处理大文件

    在处理大文件时,如果利用普通的FileInputStream 或者FileOutputStream 抑或RandomAccessFile 来进行频繁的读写操作,都将导致进程因频繁读写外存而降低速度.如 ...

  9. npm常用命令小结

    目录(更新于2016.09.23): 1.认识和使用NPM 2.npm包安装模式 3.npm包管理(package的安装.卸载.升级.查看.搜索.发布,其他等) npm install [-g] 本地 ...

  10. C#编写抽奖问题

    输入每个人的中奖号码,进行滚动显示    //清屏  //随即一个索引   // 根据索引打印  //等待0.1秒            Console.Write("请输入参与者人数:&q ...