news 2026/7/15 19:52:10

条款10:优先选用限定作用域enum,而非不限定作用域enum

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
条款10:优先选用限定作用域enum,而非不限定作用域enum

目录

条款10:优先选用限定作用域enum,而非不限定作用域enum(Prefer scoped enum to unscoped enum)

优点分析

优点1(作用域与命名空间):enum class可以避免(外围)名字空间污染。

优点2(隐式转换与类型安全):enum class可以避免隐式类型转换。

优点3(底层类型与前置声明):enum class可以前置声明(降低编译依赖)。

缺点分析

选择之道


条款10:优先选用限定作用域enum,而非不限定作用域enum(Preferscoped enumtounscoped enum

枚举类型:内置类型,允许定义一组具有特定名称的常量,枚举类型的值在编译时被解析为整数。

  • 不限定作用域的枚举类型(unscoped enum):通过enum声明(C++98)
    • 普通枚举、传统枚举、弱类型枚举
  • 限定作用域的枚举类型(scoped enum):通过enum classenum struct声明(C++11)
    • 枚举类、强类型枚举

枚举类型的值:枚举值 = 枚举量 = 枚举成员

优点分析

优点1(作用域与命名空间)enum class可以避免(外围)名字空间污染

理论依据:通常而言,如果在“大括号{}”里声明一个名字,则该名字的作用域(可见性)就被限定在“大括号{}”之内。然而,这个规则不适用于C++98风格的枚举类型中定义的枚举量。

概念辨析:枚举类型所在的作用域 = 包含着枚举类型的作用域 = 外围命名空间

enum:没有定义新的作用域,枚举量属于包含着枚举类型的作用域(即枚举量的作用域和枚举类型相同),因此会泄漏(污染)到枚举类型所在的作用域。这意味着,在此作用域内不能含有相同名字的其他实体,否则会导致编译错误或逻辑混淆。

enum class:定义新的作用域,枚举量只属于枚举类型内部的作用域(即枚举量的作用域被限定在枚举类型内),因此不会泄漏(污染)到枚举类型所在的作用域

优点2(隐式转换与类型安全)enum class可以避免隐式类型转换

enum:枚举量可以通过隐式类型转换整数类型(并能够从此处进一步转换为浮点类型),可能引发未预期的行为。(类型不安全弱类型枚举

  • if条件判断:“枚举量red”可以从Color(enum枚举类型)”隐式类型转换为int(整数类型)”,并进一步地转换为double(浮点类型)”,从而实现将“Color类型”和“double类型”的值进行比较
  • 函数调用:“枚举量red”可以从Color(enum枚举类型)”隐式类型转换为std::size_t(整数类型)”,从而实现计算一个“Color类型”的质因数
  • 程序分析:由上述分析可知,尽管下述代码语法合规(能通过编译)但是语义歪曲(不自然)

enum class:枚举量只能通过强制类型转换(static_cast其他类型(不存在任何的隐式转换路径),避免意外的类型转换错误。(类型安全强类型枚举

优点3(底层类型与前置声明)enum class可以前置声明(降低编译依赖)

底层类型(underlying type所有的枚举类型都有一个整数类型作为其底层类型(或者是默认底层类型;或者是显式指定底层类型;或者是编译器根据定义自动选择)。

前置声明(forward declaration枚举类型先于枚举量声明。(等价表述,在指定枚举量之前直接声明枚举类型。)

理论依据(关系):只有当编译器能够确定枚举类型的底层类型时(最终目的是确定其尺寸),才能使用枚举类型。

enumC++98:由于既没有默认底层类型也不支持显式指定底层类型,因此不可以前置声明。换言之,C++98仅支持“枚举类型定义”(即列出所有的枚举量,以便编译器根据定义来确定底层类型),不允许“枚举类型声明”

  • 程序分析对于Color,由于只有三个值需要表示,因此编译器可能选择char作为底层类型。对于Status,由于枚举类型的取值范围较大(例如,此处需要表示从0到0xFFFFFFFF),因此编译器通常会选择比char更大的整数类型作为底层类型(除非是在不常见的机型上,其中char至少包含32bits)。

enumC++11:虽然没有默认底层类型但支持显式指定底层类型,因此(条件)可以前置声明(必须显式指定底层类型)。

enum classC++11:由于既有默认底层类型(int),也支持显式指定底层类型,因此(总是)可以前置声明(无论是否显式指定底层类型)。

问题:前文指出,C++98的枚举类型(即enum)仅支持“枚举类型定义”(即列出所有的枚举量),不允许“枚举类型声明”。请问这种设计(前置声明的缺失有何缺点

回答1:在C++98中,枚举(enum)无法前置声明,最大缺点就是增加编译依赖性。考虑enum类型Status,这种枚举很可能会在整个系统中被广泛使用,因此通常会被放在一个头文件中,而系统中的各个部分都会依赖这个头文件。假设后来Status增加了一个新状态值,很可能整个系统都必须重新编译,即使实际上只有一个子系统(甚至可能只有一个函数)使用了这个新的枚举值。

回答2:在C++11中,枚举(enum class和enum)允许前置声明,从而可以消除上述问题。首先,如果修改了Status的定义,则包含下述声明的头文件就不需要重新编译。其次,如果修改了Status的定义(例如,增加枚举值audited),但continueProcessing的行为并没有受到影响(例如,因为continueProcessing根本没有使用audited),则continueProcessing的实现文件也不需要重新编译。

缺点分析

鉴于enum class既能避免命名空间污染,又能避免隐式类型转换,人们很容易认为它在任何情况下都优于enum。然而,实际上至少有一种场景,enum仍然具有优势并非完全没有用武之地),那就是(在C++11中)使用enum枚举值来表示std::tuple各个字段的索引(给std::tuple各个字段进行命名)。这是因为std::get的模板参数需要整数常量,enum恰好能够隐式转换为std::size_t。

案例分析:假设我们需要为社交网站准备一个std::tuple,用于持有名字电子邮件声望值

实现1(不推荐)

  • 程序设计:访问元素时,使用“整数常量”作为std::get的模板类型参数。

  • 问题分析using别名声明中,注释清楚地指示出tuple各个字段的对应含义。然而,如果在另一个源文件中看到类似“本实现”的代码,这些注释并没有多大帮助。作为程序员,你是否应该记住“第一个字段”代表“用户的电子邮件”吗?(不应该,增加记忆负担

实现2(推荐)

  • 程序设计:访问元素时,使用enum的枚举值”作为std::get的模板类型参数。

  • 优点分析:由于“enum枚举类型”能够隐式类型转换为“std::size_t整数类型”(正是std::get要求的类型),将“字段编号”与“字段名称”关联起来,因此可以避免直接写“魔法数字”,消除记忆需求

实现3.1(不推荐)

  • 程序设计:访问元素时,使用enum class的枚举值”作为std::get的模板类型参数。

  • 问题分析:由于“enum class枚举类型”不能隐式类型转换为其他类型,因此需要(使用static_cast)强制类型转换为“std::size_t整数类型”,代码啰嗦。(若不想那么啰嗦,就得写个函数,见“实现3.2”。)

实现3.2(不推荐)

  • 辅助函数(设计思路):传入枚举值,返回其对应的std::size_t值,但这有一点技巧性。
    • 首先,由于std::get函数模板,因此传入的(std::size_t类型)模板类型参数(注意此处使用的是<>而不是())。这意味着,(将枚举量由“UserInfoFields”变换为“std::size_t”值的)函数必须在编译期间就计算出结果,因此必须使用constexpr函数(见“条款15”)。
    • 其次,由于函数应该能够应用于任何枚举类型,因此(光是constexpr 函数还不够)还得用constexpr函数模板
    • 接着,由于返回值也需要进行泛化,因此(不能返回std::size_t)需要返回枚举类型的底层类型(可通过类型特征std::underlying_type获得,见“条款9”)。
    • 最终,由于函数肯定不会产生异常,因此还要将其声明为noexcept(见“条款14”)。
  • 辅助函数(具体实现):函数模板toUType接受任意类型的枚举量,并以编译期常量返回其(对应的底层类型的)值。

  • 程序设计:访问元素时,使用enum class的枚举值”作为std::get的模板类型参数。

  • 问题分析:即使可以借助辅助函数来简化类型转换代码,使用enum class还是比enum打的字要多。
  • 补充说明:enum class可以避免命名空间污染和不经意间地隐式类型转换。在大多数情况下,你应该会觉得多敲几个(几行)字符是合理的代价(是值得的),以避免enum技术带来的陷阱。

选择之道

  • enum:需要兼容C语言代码 或 需要与旧系统交互
  • enum class:需要更好的作用域控制和类型安全,并且编译器支持C++11及以上版本

enum(传统枚举)

enum class(强类型枚举)

作用域

没有定义新的作用域

定义新的作用域

访问方式

可以直接访问,无需添加前缀

不能直接访问,需要添加作用域

维护扩展

不易

容易

类型安全

不安全

安全

打印输出

直接cout

需要进行显式类型转换

底层类型

允许明确指定

int

适用场景

需要兼容C语言代码

需要与旧系统交互

需要更好的作用域控制和类型安全(前提:编译器需要支持C++11及以上版本)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/15 19:51:33

每天认识一种投资品类:期权

文章目录1.什么是期权&#xff1f;2.期权的基本要素3.期权的两种基本类型4.期权的买方与卖方5.期权的价格由什么组成&#xff1f;6.期权的主要策略7.期权的核心风险8.期权 vs 期货9.谁适合交易期权&#xff1f;10.总结参考文献期权&#xff0c;是一种赋予买方在未来约定时间内&…

作者头像 李华
网站建设 2026/7/15 19:50:18

Hitboxer终极方案:开源SOCD清洁工具深度解析与技术实现

Hitboxer终极方案&#xff1a;开源SOCD清洁工具深度解析与技术实现 【免费下载链接】socd Key remapper for epic gamers 项目地址: https://gitcode.com/gh_mirrors/so/socd 在竞技游戏的世界里&#xff0c;每一次按键都关乎胜负。当你在《空洞骑士》中同时按下左右方向…

作者头像 李华
网站建设 2026/7/15 19:42:03

2026年高考,邵阳这些口碑好的高考复读学校,你知道几个?

高考是人生的重要转折点&#xff0c;对于一些考生来说&#xff0c;复读是实现梦想的二次机会。在邵阳&#xff0c;有不少口碑良好的高考复读学校&#xff0c;其中邵阳湘郡铭志学校高复部尤为突出。下面就为大家详细介绍一下这所学校以及复读需要关注的要点。一、湘郡铭志学校高…

作者头像 李华
网站建设 2026/7/15 19:41:11

ChatGPT 表格如何导出 word,AI 导出鸭破解格式错乱难题实现无损导出

关键词&#xff1a;ChatGPT表格导出、Word格式转换、Markdown表格、无损导出、AI 导出鸭、多终端文档转化 引言 当下职场、科研人群高频使用ChatGPT生成数据表格&#xff0c;但原生不支持直出Word&#xff0c;各类传统导出方式普遍存在排版崩坏、数据错位痛点&#xff0c;AI 导…

作者头像 李华