当前位置: 首页 > news >正文

转向现代C++——优先选用限定作用域的枚举型别,而非不限作用域的枚举型别

文章目录

    • 优先选用限定作用域的枚举型别,而非不限作用域的枚举型别
      • 名字空间污染
      • 强类型安全与隐式转换
      • 前置声明
      • 特例:什么时候不限作用域的 enum` 更好?
        • 现代 C++ 的替代方案(C++17 结构化绑定)

优先选用限定作用域的枚举型别,而非不限作用域的枚举型别

在 C++98 中,我们使用的 enum 是不限作用域的(unscoped enum);而 C++11 引入了通过 enum class` 声明的限定作用域的枚举(scoped enum)

绝大多数情况下选择 enum class。它能帮你屏蔽掉大量由于隐式类型转换和命名冲突导致的低级 Bug。

名字空间污染

不限作用域的枚举(enum)中定义的名字,会直接泄漏到该枚举所在的作用域中。这意味着你不能在同一个作用域内定义同名的枚举常量。

enumColor{black,white,red};// 编译错误!"black" 已经在这个作用域被定义过了// enum class Astronomy { black, white, red };autowhite=false;// 编译错误!"white" 已被用作枚举常量

enum class 的名字只在枚举内部可见,必须通过作用域解析运算符(::`)来访问,解决了命名冲突。

enumclassColor{black,white,red};enumclassLight{google,white,baidu};// white 不会冲突Color c=Color::black;// 必须加上 Color::Light l=Light::white;// 必须加上 Light::autowhite=false;//此时的 white 是一个独立的变量

强类型安全与隐式转换

不限作用域的枚举会极其隐式地转换为整型(甚至浮点型),这经常导致不合逻辑的代码能够通过编译。而 enum class` 是强类型的,绝不隐式转换

enumColor{black,white,red};Color c=red;// 荒谬的比较:颜色竟然可以和数字、浮点数直接比大小!if(c<14.5){// 编译通过!red 被隐式转换成了整数 2autox=c*10;// 编译通过!允许算术运算}
enumclassColor{black,white,red};Color c=Color::red;// if (c < 14.5) { } // 编译错误!不能将 Color 与 double 进行比较// if (c == 2) { } // 编译错误!不能将 Color 与 int 进行比较// 如果真的需要转换,必须进行显式类型转换(static_cast)if(static_cast<int>(c)==2){// 显式转换,安全且意图明确}

前置声明

在 C++ 中,能够前置声明一个类型可以减少头文件包含,从而加快编译速度

:::info

  • 传统 enum:通常不能前置声明。因为编译器需要知道枚举的最大值,以此来决定底层用多大的整数类型(char、int 还是 short`)来存储它。

:::

:::success

  • enum class天生支持前置声明。因为它的默认底层类型(underlying type)是固定的(标准规定默认为 int`)。

:::

// ==================== 头文件 Widget.h ==================== enum class Status; // 完美支持前置声明!不需要知道具体有哪些枚举值 class Widget { Status s; // 编译器知道 Status 默认是 int 大小(4字节),可以确定 Widget 大小 }; // ==================== 实现文件 Widget.cpp ==================== enum class Status { Good, Failed, Unknown }; // 在这里才给出具体定义

:::color4
📌**补充说明:C++11 也允许传统的 enum 进行前置声明,但你必须显式指定底层类型**,如 enum Color: uint8_t;。而enum class 不需要显式指定,默认就是 int`。

:::


特例:什么时候不限作用域的 enum` 更好?

📢**当我们需要使用std::tuple并通过语义化的名字去获取里面的元素时,传统 ****enum`的隐式转换反而成了一种“便利”。**

#include<tuple>#include<string>usingUserInfo=std::tuple<std::string,// 姓名std::string,// 邮箱std::size_t>;// 声望值// 使用传统的 unscoped enumenumUserFields{uName,uEmail,uRep};voidprocessUser(){UserInfou("Alice","alice@email.com",99);// 隐式转换为了 std::size_t,语法非常自然autoname=std::get<uName>(u);}
enumclassUserFields{uName,uEmail,uRep};voidprocessUser(){UserInfou("Alice","alice@email.com",99);// 极其恶心、冗长的语法:必须 static_castautoname=std::get<static_cast<std::size_t>(UserFields::uName)>(u);}
现代 C++ 的替代方案(C++17 结构化绑定)

在现代 C++(C++17 及以后)中,即使是上述特例,我们也不再依赖传统 enum` 了,因为我们有了结构化绑定,它比上面两种方法都更优雅:

voidprocessUser(){UserInfou("Alice","alice@email.com",99);// 直接解包,连枚举都不需要定义了auto[name,email,rep]=u;}
http://www.jsqmd.com/news/847785/

相关文章:

  • 【.NET新特性·第1篇】.NET 8:统一平台的成熟之作
  • AIGC应用工程师证书报考机构多维度实测对比 - 品牌企业推荐师(官方)
  • 26春 日总结22
  • Linux进程信息获取全攻略:从ps、top到/proc与psutil
  • 从链表到队列再到递归:三种C++解法搞定SWUST OJ#956约瑟夫问题(附完整代码)
  • 自己搭一个Java Web框架,你需要解决哪些问题
  • 从“马变斑马”到“卫星图转地图”:用CycleGAN/pix2pix玩转自定义数据集(附制作教程)
  • 告别抓瞎!手把手教你用逻辑分析仪调试SMBus电池管理通信(附BQ4050实战波形)
  • Linux网络数据包处理全流程:从系统调用到网卡驱动的深度解析
  • MySQL 单行函数笔记(日期时间函数)
  • 性价比高生产的重庆轴类加工厂哪家推荐 - 品牌企业推荐师(官方)
  • UVM验证中add_typewide_sequence与add_sequence的区别与实战应用
  • 别再乱定义坐标系了!ArcGIS数据处理中坐标系问题的终极排查手册
  • 信号处理与行为金融视角下的股价波动与量化投资建模方法【附代码】
  • 5分钟极速上手:BOTW-Save-Editor-GUI 塞尔达传说存档编辑器完整指南
  • 测试工程师的代码能力:为什么测试工程师必须会写代码
  • 推荐一款PC复制粘贴增强工具
  • 瑞萨电子2019年中国市场战略与MCU/SoC产品深度解析
  • 医生私下不告诉你的健康查询真相:Perplexity健康科普查询的3个伦理盲区与2种合规替代路径
  • AI驱动的数据库性能优化
  • 实战指南:基于F3-Net与PyTorch搭建你自己的DeepFake检测器(FaceForensics++数据集)
  • Sentinel-3A OLCI 1B 级地球观测降分辨率(ERR)数据,版本 1
  • 加密货币社区 Google 官方邮件钓鱼威胁机理与防御体系研究
  • 利润增长,是设计出来的
  • STM32G0实战:用CubeMX搞定CANFD和普通CAN双通道配置(附避坑点)
  • PCB设计避坑指南:为什么你的TTP223触摸按键不灵?从布局布线到灵敏度调节全解析
  • 刚入职Perplexity的L5工程师年薪多少?7类岗位薪资中位数+股权折算表,内推通道已同步关闭
  • Gemini Nano移动端模型裁剪内幕:Google内部benchmark未披露的3种Pruning策略对比(精度仅损0.7%)
  • 从1秒到60ms:手把手教你用STM32硬件SPI驱动GC9A01 LCD,性能飙升实战
  • 别再死记硬背公式了!用动画和Python仿真带你直观理解FOC中的Clarke/Park变换与SVPWM