C++干货系列——强枚举到底有多“强”

C++干货系列——“强”枚举有多强 c++干货 阅读详情

强枚举到底有多“强”

传统枚举类型以及其特性在开发上的弊端已逐渐显露,C++11标准对原有枚举类型进行了加强和补充,从而衍生出了"强枚举"。本文不仅旨在讲清楚强枚举是如何在弱枚举上进行改进,更希望阐明弱枚举的种种不足是如何对开发造成不利影响的;以及同时C++11"新标准"(早已不新)是如何对98的枚举进行补充的。

枚举无处不在

在工程代码当中,只要和数据打交道,基本上就离不开枚举类型——这是因为单纯的字符串类型在一份合格的工程代码中是不应该出现的,所有的字符串都应以枚举或常量的形式定义。举个例子,如果我们有一个图像类,其中有两个成员分别代表了该图像的类型和分辨率,那么如果写成以下形式,那就要再去看一下工程规范了。

// Image.h
// 定义
class Image{
private:
    String imgType;
    int resolution;
public:
    Image();
    Image(String imgType, int resolution);
  
/**
 * 其他成员已省略...
**/
};

// Process.cpp
Image tmpImage = new Image("jpg", 1920 * 1080);

上边调用是危险的,准确的来说问题出现在Image类的定义处,没有对两个成员的初始化作强限制。例如第一个参数调用者可能将参数传为"JPG",就意思表达来说是准确的,但是会给代码带来极大的不确定性;后者也可被传入诸如1080 * 1920之类的不符合要求的值,即使在备注上加上说明,也不能保证这是一个良好的设计。

以上所有的传参都应被常量或者枚举所替换,如对于图片的类型,我们可以设计一个枚举,从而存放可能需要的类型,而在类中成员,我们将其定义为枚举类型。

enum ImageType{
    JPG = 100,
    PNG = 200,
    TIFF = 300,
    SVG = 400
};

enum ImageResolution{
    HIGH = 1920 * 1080,
    HIGH_2K = 2560 * 1440,
    HIGH_4K = 4096 * 2160
};

class Image{
private:
    ImageType imgType;
    ImageResolution resolution;
public:
    Image();
    Image(String imgType, ImageResolution resolution);
  
/**
 * 其他成员已省略...
**/
};

C++98的弱枚举

请看几个问题。

首先,在上例中,传统枚举的写法允许你这样写:

Image tmpImage = new Image(ImageType::JPG, ImageResolution::HIGH_2K);

但同时,他还允许你这样写:

Image tmpImage = new Image(JPG, HIGH_K);

在C++98枚举(弱枚举)中,枚举类型是不限定作用域的(unscoped enumeration),枚举中的成员可不加命名空间限定符随意使用,但是不限定作用域的做法总是充满危险的,就上例而言,我们无法保证在当前命名空间是否不存在与ImageResolution相对应的CameraResolution枚举类型,其同样包含有成员HIGH, HIGH_2K, HIGH_4K中的一个或多个,若真的包含,很可能在不知觉中被调用者混淆——更让人难过的是,调用者可能并没有料到如此。这种场景在使用第三方库的情况下更为严重。因为传统枚举总是默认可以被隐式地转化为int类型,在不知不觉中就进行了跨作用域的类型转换。

说到隐式转换,这就不得不让人提到另一个让人心塞的问题。对于int类型所能表达的最大范围是2147483647,如果我们在显示得给枚举成员赋值时超出这个值呢?如:

enum annoyType{
    BIG_INTEGER = 2147483648;    // 这里会发生什么,会报错吗?
}

很遗憾,不会报错,而且什么事都不会发生。你可能会问,会有人这么做吗?在真实的应用场景中,很难保证人们不会这么写:很大程度上是因为等号后边的表达式并不那么显而易见。C++ 98的枚举类型很弱,可以进行对整形和浮点型的隐式转换,这很容易造成滥用——而且你并没有办法限定枚举的基类型为int或其他,上述的不确定性在下边的例子更显而易见。

// Debug的为32位整数,LogLevel的基类型可能为int
enum LogLevel{
    Fatal,
    Error,
    Warning,
    Debug = 0xFFFFFFFF
};

// 枚举成员最大值为2, Color的基类型可能为char
enum TrafficLightColor{
    red,
    yellow,
    green
}

以上代码编译错误,因为编译器无法知道LogLevel所需要的内存大小。重点在于若编译器不知道枚举成员的取值范围,就不知道如何设置枚举类型的基类型。C++是强类型语言,规定在使用任何类型之前,需要看到这个类型的声明,并且可以根据此声明推断出该类型所占内存的大小。否则无法通过编译。由于C++98枚举类型的大小必须在看到它的定义后才能知道,因此无法实现前置声明。因此,弱枚举类型必须定义在头文件中——编译依赖由此产生。假设有两个文件都包含了business.h一个是business.cpp用来实现接口,另一个client.cpp用来使用接口。如果修改了LogLevel的定义,如增加一个成员等级为Info,就需要将business.cpp和client.cpp都重新编译,如果能实现前置声明,就可以将LogLevel的定义放在business.cpp中,当LogLevel的定义改变时,只需重新编译business.cpp即可,提高编译效率。

强枚举“强”在哪

强枚举官方限定名称为[ScopedEnumerations],定义强枚举需要在enum关键字后面加上class或者struct关键字。通过对弱枚举的分析,强枚举的功效就比较明显了。

一、限定可见性

强枚举的枚举成员可见性仅限定在枚举类型内:

enum color {red, yellow, green};    //不限定作用域的枚举类型
enum stoplight {red, yellow, green};//错误:重复定义了枚举成员
enum class pepers {red, yellow, green}; //正确:枚举成员被隐藏了
color eyes = green; //正确
peppers p = green;  //错误:pepeers的枚举成员不在有效作用域中,
                    //green对应的是color::green,但显然类型错误。
peppers p2 = peppers::red;          //正确使用peppers的red

二、更强的类型

强枚举是更强的枚举类型,不支持隐式转换为其他类型,如果要转换类型,必须显示地使用强制类型转换static_cast。

int i = color::red; //正确,不限定作用域的枚举类型的枚举成员隐式地转换成int
int j = peppers::red; //错误,限定作用域枚举类型不会进行隐式转换。
int x = static_cast<int>(peppers::red); //正确

需要指出的是,尽管强制类型转换可以完成这样的”壮举“,但这并不意味着C++鼓励你去这么做——恰恰相反,这种限制希望你把不同的类型区分开,所以除非在一些项目历史原因导致的不合理的设计一定要用到这样的写法,则尽量避免。如果需要进行枚举成员之间的某种逻辑上的大小比较,可以直接规定清楚每个枚举成员的值,再进行直接比较。

enum class LogLevel{
    Fatal = 500,
    Error = 400,
    Warning = 300,
    Debug = 200,
    Info = 100
};

// This is OK
LogLevel logLevel1 = LogLevel::Fatal;
LogLevel logLevel2 = LogLevel::Info;
if(loglevel1 > logLevel2){
    // Do something...
}

三、限定基类型和前置声明

这两点其实并不属于强枚举的”专利“,即使使用弱枚举,C++11也对他进行了改进并使其适应这样的写法。

enum intValues : unsigned long long {
    charTyp = 255, shortTyp = 65535, intType = 2147483647,
    longTyp = 4294967295UL,
    long_longTyp = 18446744073709551615ULL
};

如果我们没指定枚举的潜在类型,在强枚举中成员类型默认为int,而对弱枚举而言,枚举成员不存在默认类型,只知道成员的潜在类型足够大,肯定能够容纳枚举值。

但是:一旦定义了某个枚举成员的基类型(包括强枚举的隐式限定),一旦某个枚举成员的值超出了该类型所能容纳的范围,则会引发程序错误。

指定enum潜在类型的好处在于:

可以控制不同实验环境中使用的类型,我们可以确保在一种实现环境中编译通过的程序所产生的代码与其他实现环境中产生的代码一致。
——《C++ Primer》

同样的对于前置声明,以下写法将被接受:

// business.h

//前置声明,并隐式声明基类型为int
enum class LogLevel;

class Logger{
    // Some members
    
private:
    void logging(LogLevel level, String message);
};

// business.cpp

#include "business.h"

//枚举的实现
enum class LogLevel{
    // Implementation
};

// client.cpp

#include "business.h"

//调用
void someFunc(LogLevel level){
    // Invokation
}

总结

  • 弱枚举的成员可见性与定义它的枚举相同;强枚举的可见性仅为枚举体内。
  • 弱枚举可以隐式类型转换,但强枚举必须采用显式,并且不建议这么做。
  • C++98中,弱枚举不支持前置声明;C++11中强、弱枚举都支持。
  • C++98中,弱枚举不支持基类型定义,枚举类型;C++11中强、弱枚举都支持,且强枚举默认基类型为int。
C++干货系列——从编译期常量谈到constexpr(二) C++11标准前的编译期运算 在第一篇文章中,我把主要精力放在了什么是编译期常量,以及编译期常量有什么作用上。在这一篇文章中,我将更详细地介绍编译期常量是如何产生的。之所以要把编译期常量了解的这么透彻,是因为他是编译期运算的基础。在这篇文章中还会讲解我们在C++11 标准前都可以用做哪些**编译期运算(**compile-time calculations),通过了解这些比较原始的方法,我们能够更好地理解C++11标准为编译期运算方面所做的工作。 编译期常量都从哪里来? 在我们的经验中,大部分编译期常量的来 阅读详情

相关推荐

C++干货系列——从编译期常量谈到constexpr(一)

从编译期常量谈起 C++20都支持虚函数的constexpr了,我打算用三篇读文章讲清楚编译期常量和constexpr这个东西和编译期常量的关系,即为什么需要他来辅助解决这个问题。最后帮助读者在实际编码过程中能够有意识地去运用他们,这才是终极目标。这篇文章中会讲到隐藏在日常编程中的各种编译期常量,以及他们存在的意义。 什么是编译期常量 想要用编译期常量就要首先知道它们是什么,一般出现在哪里和运行期常量有什么区别,因此我打算用第一篇文章重点分析编译期常量以及使用他们有什么好处。 编译期常量(Compile-t

ColdWindHA的博客 1137

C++枚举

枚举类型(enumeration)是 C++ 中的一种派生数据类型,它是由用户定义的若干。除了定义枚举常量,也可以同时定义。

MWooooo的博客 3763

C++ 知识补充

向量(vector)是一个封装了动态大小数组的顺序容器(SequenceContainer)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。C++中map提供的是一种键值对容器,里面的数据都是成对出现的,如下图:每一对中的第一个值称之为关键字(key),每个关键字只能在map中出现一次;第二个称之为该关键字的对应值。

weixin_47059239的博客 2130

c++枚举

一、枚举类型的定义 枚举类型的定义:枚举类型(enumeration)是 C++ 中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。 定义格式:枚举类型的定义格式为:enum <类型名> {<枚举常量表>}; 例如: enum color_set1 {RED, BLUE, WHITE, BLACK}; // 定义枚举类型color_set1 enum week {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; // 定义枚举类型week 二、枚举声.

顾道长生的科研笔记 2万+

c++中的enum类型

C++允许程序员创建自己的数据类型,比如本节要将的枚举类型。枚举数据类型是一种由程序员定义的数据类型,其合法值是与它们关联的一组命名整数常量。 之所以被称为枚举类型,就是因为命名常量是作为数据类型定义的一部分而枚举或列出的,以下是枚举类型声明的示例: enum Roster {Tom, Sharon, Bill, Teresa, John}; 该语句将创建一个名为 Roster 的数据类型。...

digitalkee的博客 2万+

C++知识点查阅

1.GLog 是一个应用程序级的日志记录的库

小勇博客 531

C++: 枚举

不论是哪种形式的枚举,它们都定义了一种独特的数据类型,可以用来创建变量,这些变量只能取预先定义好的枚举成员之一作为值。中枚举类型分为弱枚举类型和强枚举类型,不论是哪种形式的枚举,它们都定义了一种独特的数据类型,可以用来创建变量,这些变量只能取预先定义好的枚举成员之一作为值。强枚举类型的值不会自动转换为整数或其他枚举类型,避免了无意中的类型混淆和错误的算术运算,增强了类型安全性。:强枚举类型的枚举成员被限定在其定义的作用域内,不会污染全局命名空间,减少了命名冲突的可能性。强枚举类型和弱枚举类型。

qq_54375651的博客 739

用Navicat轻松管理魔兽世界私服数据库:修改IP、添加装备的实用技巧

本文详细介绍了如何使用Navicat管理魔兽世界私服数据库,包括修改服务器IP、添加自定义装备和调整角色属性等实用技巧。通过专业的SQL操作指南,帮助玩家轻松实现私服的高级定制,提升游戏体验。

fanta的博客 938

C++强枚举类型

在 C++ 中,“强枚举”(Strong Enum)通常指使用 enum class 定义的枚举类型(C++11 引入)。相比传统枚举(enum),它具有更严格的类型安全和作用域控制。Value2,// ...enum class:关键语法,声明强枚举EnumName:枚举类型名称UnderlyingType(可选):底层整数类型(如 int, unsigned char 等),默认是 intValueX:枚举值,可显式赋值或自动递增。

肩上风骋的博客 579

无感FOC之高频注入法——永磁同步电机控制

1.摘要电机运行在低速或零速时,磁链产生的反电动势很微弱,可用信号的信噪比太低,因而难以从反电动势中提取转子的位置和速度信息。因而在电机处于低速或零速时,需要借助电机的凸极特性来获取转子的位置信息。内置式永磁同步电机或磁密较大的表贴式永磁同步电机具有这种凸极特性,可借助高频电压注入法获取转子位置。2.凸极特性凸极电机内部的气隙是不均匀的,d轴和q轴的有效气隙是不同的,通俗来说在凸极电机里,q轴电感往往大于d轴电感,而在大部分表贴式永磁同步电机,气隙分布相对均匀,q轴电感近似于d轴电感。凸极率常用q轴电感和d

qq_2545470942的博客 2万+

【推荐算法】召回模型总结

【推荐算法】召回模型总结

m0_48086806的博客 6241

ef 在此上下文中只支持基本类型或枚举类型_C++干货系列——“强”枚举有多强

传统枚举类型以及其特性在开发上的弊端已逐渐显露,C++11标准对原有枚举类型进行了加强和补充,从而衍生出了"强枚举"。本文不仅旨在讲清楚强枚举是如何在弱枚举上进行改进,更希望阐明弱枚举的种种不足是如何对开发造成不利影响的;以及同时C++11"新标准"(早已不新)是如何对98的枚举进行补充的。枚举无处不在在工程代码当中,只要和数据打交道,基本上就离不开枚举类型——这是因为单纯的字符串类型在一份合格的...

weixin_39524574的博客 95

YOLO26跨时代升级!YOLO26 MLCA注意力机制解读

摘要: 本文提出了一种基于多级上下文聚合(MLCA)注意力机制的改进卷积神经网络。MLCA模块通过局部和全局注意力机制动态调整特征图权重,结合了ECA注意力与自适应池化,显著提升特征表达能力。实验表明,添加MLCA后模型mAP提升12.11,准确率提高5.43。文中详细解析了MLCA1模块的工作原理,包括局部/全局池化、卷积操作和注意力加权机制,并介绍了集成MLCA的Bottleneck_MLCA模块。该机制通过多层次特征融合,在保持计算效率的同时增强模型对关键特征的敏感性。最后提供了YOLOv26-MLC

m0_71589036的博客 282

运行时常量池在哪里_C++干货系列——从编译期常量谈到constexpr(二)

在第一篇文章中,我把主要精力放在了什么是编译期常量,以及编译期常量有什么作用上。在这一篇文章中,我将更详细地介绍编译期常量是如何产生的。之所以要把编译期常量了解的这么透彻,是因为他是编译期运算的基础。在这篇文章中还会讲解我们在C++11 标准前都可以用做哪些编译期运算(compile-time calculations),通过了解这些比较原始的方法,我们能够更好地理解C++11标准为编译期运算方面...

weixin_39559015的博客 103

自动化Ai Agent全流程实现【设计+代码】

本文探讨了人工智能代理(AI代理)如何通过自动化复杂的业务流程来帮助企业提高效率和降低成本。AI代理具有适应性、智力、自主性和可扩展性等特点,能够处理非结构化信息、在定义参数范围内做出判断、协调多个系统或利益相关者,以及处理变量输入和异常。文章详细介绍了AI代理在文件处理、客户服务、人才招聘和财务运作等领域的应用,并提供了一个逐步实现发票处理自动化的案例。此外,文章还强调了实施AI代理自动化的优势,包括效率提升、成本降低、准确性和可扩展性的改进,并提出了实施最佳实践,如从高影响力流程开始、设计人机交互、优先

FeelTouch Labs 895

某勾网之登录过程逆向

拉勾网的登录过程逆向,包含请求头(X-S-HEADER、X-K-HEADER、X-SS-REQ-HEADER等) 以及 请求参数/返回数据的加解密。

shanf7921的博客 3万+

运行时常量池在哪里_C++干货系列——从编译期常量谈到constexpr(一)

C++20都支持虚函数的constexpr了,我打算用三篇读文章讲清楚编译期常量和constexpr这个东西和编译期常量的关系,即为什么需要他来辅助解决这个问题。最后帮助读者在实际编码过程中能够有意识地去运用他们,这才是终极目标。这篇文章中会讲到隐藏在日常编程中的各种编译期常量,以及他们存在的意义。从编译期常量谈起想要用编译期常量就要首先知道它们是什么,一般出现在哪里和运行期常量有什么区别,因此我...

weixin_39636079的博客 202

http协议

HTTP 协议由起始行、头部、空行、消息体组成。

qq_34059233的博客 88

c枚举类型enum例题_一篇文章让你详细了解Java中Enum枚举类的使用

文章前记程序员工作久了便可能整日忙碌于“增删改查”中,迷失方向,毫无进步。该公众号致力于分享软件开发相关的原创干货,助你完成从程序员到架构师的进阶之路!努力!做一个NB的Coder!本文是Java枚举类型的第三篇文章,对于枚举类型的实现原理和包含的方法特性不了解的小伙伴可以先去参考历史文章中的《Java中的枚举类型(Enum)详解》和《详解Java枚举类型(Enum)中的方法》两篇文章。接下来,我...

weixin_39844549的博客 85

把 SM30 用顺:从表设计到权限与传输,全面掌握 SAP Table Maintenance Generator

摘要 SAP的Table Maintenance Generator(TMG)是为配置表维护需求设计的经典工具,能够生成标准化的维护对话框(SM30)。它支持单表、维护视图(多表关联)和视图簇(导航式维护)三种维护对象类型。使用TMG前需注意三个关键设置:1)表维护权限选项;2)交付类(决定数据传输行为);3)客户端字段配置。通过SE54/SE11生成维护对话框时,需选择维护类型(一步/两步)和记录例程(控制传输)。TMG生成的对象包含函数组、屏幕和处理模块,但部分标准框架不可修改。实际应用中,可通过参数事

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 176

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

C++进阶之enum指定类型(四百二十九)

本篇目的:C++枚举指定int类型之用法实例基本概念使用enum关键字并显式指定底层整数类型,保证枚举值按指定字节宽度存储,可与整数无缝转换。功能提供强类型别名、位运算安全、序列化友好、接口传参精确控制。使用限制值不可越界;需C++11及以上;类型必须能表示所有枚举值。性能特性与对应整数类型同尺寸;编译期常量;无运行时开销。使用场景协议字段、位掩码、状态机、跨语言接口、内存映射文件。应用场景图像处理中限制颜色分量范围0到255。用法实例int输出150应用场景。

Android系统攻城狮 575

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

C++_12——新特性(一些类型、关键字、表达式)

C++_12——新特性(一些类型、关键字、表达式) char16_t / char32_t char16_t char32_t 无符号,长16位 无符号,长32位 前缀u 前缀U size_t size_t在不同架构下的定义不同。 32位系统下一般定义为unsigned int ,占4字节;64位系统下一般定义为unsigned long,占8字节。 size_t方便移植程序,如计数等操作,保证能容纳实现所建立的最大对象的字节大小。 long long 和 unsig

fangfang12138的博客 718

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

【C/C++】深入详解内置类型和自定义类型

自定义类型 (User-defined Types)。

艾莉丝努力练剑的博客,感谢大家一直以来的对艾莉丝的支持和鼓励,uu们如果对C/C++方向感兴趣的话,可以通过艾莉丝主页的微信名片加艾莉丝的好友,和努力练剑的艾莉丝一起加油! 1738

C++11标准之重要特性及用法实例(二百八十一)

本篇目的:C++11之重要特性及用法实例C++11,也称为C++0x,是C++编程语言的一个重要标准,于2011年正式发布。C++11相较于之前的C++98/03标准,进行了大量更新和改进,使得C++语言在易用性、性能和安全性方面有了显著提升。以下是C++11的主要特点:自动类型推导:C++11引入了auto和decltype关键字,使得类型推导更加方便。auto可以自动推导变量类型,而decltype可以获取表达式的类型。

Android系统攻城狮 1241

C++之C++11引入enum class与传统enum关键字区别总结(二百五十一)

本篇目的:理解C++之lambda匿名函数、typedef、using等用法C++11引入的enum class关键字来定义枚举传统的enum关键字定义枚举作用范围:enum class创建了一个作用域限定的枚举类型,而enum则创建了一个隐式的全局作用域枚举类型。这意味着使用enum class定义的枚举成员在枚举之外是不可见的,需要通过作用域解析运算符(::)来访问。默认类型安全性:enum class提供了更严格的类型安全性,不允许隐式的整数转换。

Android系统攻城狮 625
上一篇: C++干货系列——右值引用与移动语义
下一篇: C++干货系列——谈谈在变量中使用auto
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值