详解C++11强类型枚举

1.传统枚举类型的缺陷

枚举类型是C/C++中用户自定义的构造类型,它是由用户定义的若干枚举常量的集合。枚举值对应整型数值,默认从0开始。比如定义一个描述性别的枚举类型。

enum Gender{Male,Female};

其中枚举值Male被编译器默认赋值为0,Female赋值为1。传统枚举类型在设计上会存在以下几个问题。

(1)同作用域同名枚举值会报重定义错误。传统C++中枚举常量被暴漏在同一层作用域中,如果同一作用域下有两个不同的枚举类型,但含有同名的枚举常量也是会报编译错误的,比如:

enum Fruits{Apple,Tomato,Orange};
enum Vegetables{Cucumber,Tomato,Pepper}; //编译报Tomato重定义错误

其中水果和蔬菜两个枚举类型中包含同名的Tomato枚举常量会导致编译错误。因为enum则是非强作用域类型,枚举常量可以直接访问,这种访问方式与C++中具名的namespace、class/struct以及union必须通过"名字::成员名"的访问方式大相径庭。

(2)由于枚举类型被设计为常量数值的“别名”,所以枚举常量总是可以被隐式转换为整型,且用户无法为枚举常量定义类型。

(3)枚举常量占用存储空间以及符号性不确定。C++标准规定C++枚举所基于的“基础类型”是由编译器来具体实现,这会导致枚举类型成员的基本类型存在不确定性问题,尤其是符号性问题,即。考察如下示例:

enum A{A1=1,A2=2,ABig=0xFFFFFFFFU};
enum B{B1=1,B2=2,BBig=0xFFFFFFFFFUL};

int main()
{
	cout<<sizeof(A1)<<endl;	//4
	cout<<ABig<<endl;		//4294967295
	cout<<sizeof(B1)<<endl;	//8
	cout<<BBig<<endl;		//68719476735
}

以上输出结果是在Linux平台下使用g++编译输出的结果,在VC++(VS2017)中的输出结果分别是4、-1、4和-1。可见不同编译器对枚举常量的整型类型的宽度和符号有着不同的实现。GNU C++会根据枚举数值的类型使用不同宽度和符号的整型,VC++则始终以有符号int来表示枚举常量。

为了解决以上传统枚举类型的缺陷,C++11引入了强类型枚举解决了这些问题。

2.强类型枚举

非强作用域类型,允许隐式转换为整型,枚举常量占用存储空间以及符号性的不确定,都是枚举类缺点。针对这些缺点,C++11引入了一种新的枚举类型——强类型枚举(strong-typed enum)。

强类型枚举使用enum class语法来声明:

enum class Enumeration{VAL1,VAL2,VAL3=100,VAL4};

强类型枚举具有如下几个优点:
(1)强作用域,强类型枚举成员的名称不会被输出到其父作用域,所以不同枚举类型定义同名枚举成员编译不会报重定义错误。进而使用枚举类型的枚举成员时,必须指明所属范围,比如Enum::VAL1,而单独的VAL1则不再具有意义;
(2)转换限制,强类型枚举成员的值不可以与整型发生隐式相互转换。比如比如Enumeration::VAL4==10;会触发编译错误;
(3)可以指定底层类型。强类型枚举默认的底层类型是int,但也可以显示地指定底层类型。具体方法是在枚举名称后面加上":type",其中type可以是除wchar_t以外的任何整型。比如:

enum class Type:char{Low,Middle,High};

注意:
(1)声明强类型枚举的时候,既可以使用关键字enum class,也可以使用enum struct。事实上,enum struct与enum class在语法上没有任何区别。
(2)由于强类型枚举是强类型作用域的,故匿名的enum class可能什么都做不了,如下代码会报编译错误:

enum class{General,Light,Medium,Heavy}weapon;
int main()
{
weapon=Medium; //编译出错
bool b=weapon == weapon::Medium; //编译出错
return 0;
}

当然对于匿名强类型枚举我们还是可以使用decltype来获得其类型并进而使用,但是这样做可能违背强类型枚举进行匿名的初衷。

3.C++11对传统枚举类型的扩展

传统枚举类型为了配合C++11引入的强类型枚举,C++11对传统枚举类型进行了扩展。
(1)底层的基本类型可以在枚举名称后加上":type",其中type可以是除wchar_t以外的任何整型,比如:

enum Type:char{Low,Middle,High};

(2)C++11中,枚举类型的成员可以在枚举类型的作用域内有效。比如:

enum Type{Low, Middle, High };
Type type1 = Middle;
Type type2 = Type::Middle;

其中Middle与Type::Middle都是合法的使用形式。

以上就是详解C++11强类型枚举的详细内容,更多关于c++ 枚举的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++小知识:尽可能使用枚举类

    静态代码分析工具可简化编码过程,检测出错误并帮助修复.PVS-Studio 是一个用于 C/C++ 的静态代码分析工具.该团队检测了 200 多个 C/C++ 开源项目,包括了 Unreal Engine.Php.Haiku.Qt 和 Linux 内核等知名项目. 下面这个 Bug 是在 Source SDK 的源代码中发现的. 错误代码: 这种错误的例子代码量都非常大,我尽可能地选取其中最小的一部分,但是很抱歉,代码看起来依旧很冗长. enum PhysGunPickup_t { PICKED

  • C++基础入门教程(四):枚举和指针

    我已经把<C++ Primer>一书准备好了,如果这本<C++ Primer Plus>继续这么瞎闹的话,我就换主角~! 没错,这书连if while都还没介绍呢,就开始把指针搬出来了,虽然只是简单介绍.. 这目录编排我也是醒了. 那么,按照书上的进度,今天来初步说说指针. 但,今天周五,你懂的,就随便水一下,因为我怕到周一又忘了. 1.枚举 那么,枚举是什么呢?(小若:等等~!说好的指针呢?) 大部分高级语言里应该都有枚举了,所以也没什么好介绍的. 使用是这样的: 复制代码 代码

  • 讲解C++中的枚举类型以及声明新类型的方法

    C++枚举类型 如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型.所谓"枚举"是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内.声明枚举类型用enum开头.例如: enum weekday{sun, mon, tue, wed, thu, fri, sat}; 上面声明了一个枚举类型weekday,花括号中sun, mon, -, sat等称为枚举元素或枚举常量.表示这个类型的变量的值只能是以上7个值之一.它们是用户自己定义的标识符. 声明枚举类

  • 结合C++11的新特性来解析C++中的枚举与联合

    枚举 枚举是用户定义的类型,其中包含一组称为枚举器的命名的整型常数. 语法 // unscoped enum: enum [identifier] [: type] {enum-list}; // scoped enum: enum [class|struct] [identifier] [: type] {enum-list}; // Forward declaration of enumerations (C++11): enum A : int; // non-scoped enum mu

  • 详解C++11强类型枚举

    1.传统枚举类型的缺陷 枚举类型是C/C++中用户自定义的构造类型,它是由用户定义的若干枚举常量的集合.枚举值对应整型数值,默认从0开始.比如定义一个描述性别的枚举类型. enum Gender{Male,Female}; 其中枚举值Male被编译器默认赋值为0,Female赋值为1.传统枚举类型在设计上会存在以下几个问题. (1)同作用域同名枚举值会报重定义错误.传统C++中枚举常量被暴漏在同一层作用域中,如果同一作用域下有两个不同的枚举类型,但含有同名的枚举常量也是会报编译错误的,比如: e

  • 详解Java中的 枚举与泛型

    详解Java中的 枚举与泛型 一:首先从枚举开始说起 枚举类型是JDK5.0的新特征.Sun引进了一个全新的关键字enum来定义一个枚举类.下面就是一个典型枚举类型的定义: public enum Color{ RED,BLUE,BLACK,YELLOW,GREEN } 显然,enum很像特殊的class,实际上enum声明定义的类型就是一个类. 而这些类都是类库中Enum类的子类(Java.lang.Enum).它们继承了这个Enum中的许多有用的方法.我们对代码编译之后发现,编译器将 enu

  • 详解C++11原子类型与原子操作

    1.认识原子操作 原子操作就是在多线程程序中"最小的且不可并行化的"操作,意味着多个线程访问同一个资源时,有且仅有一个线程能对资源进行操作.通常情况下原子操作可以通过互斥的访问方式来保证,例如Linux下的互斥锁(mutex),Windows下的临界区(Critical Section)等.下面看一个Linux环境使用POSIX标准的pthread库实现多线程下的原子操作: #include <pthread.h> #include <iostream> usi

  • 详解C++11 线程休眠函数

    C++ 11之前并未提供专门的休眠函数.c语言的sleep.usleep其实都是系统提供的函数,不同的系统函数的功能还有些差异. 在Windows系统中,sleep的参数是毫秒. sleep(2*1000); //sleep for 2 seconds 在类Unix系统中,sleep()函数的单位是秒. sleep(2); //sleep for 2 seconds 从C++11开始,中C++标准库提供了专门的线程休眠函数,使得你的代码可以独立于不同的平台. std::this_thread::

  • 详解c++11以正确的姿势输出enum class的值

    c++11新增了enum class,相比传统的enum好处多了很多,但也有些让人不太爽的地方,如:输出到std流时会报错,进行了强转则没有信息输出,那么,到底该如何将enum class的值出到std流呢? 提供这个enum class的原因是因为旧的enum有不少缺点.简单描述一下: 1. 容易被隐式转换成int 2. underlying type 指的是编译器实现者幕后的实现细节导致了跨平台,跨编译器的不一致性.尺寸的不可估计等等. 3. 没有严格的scope界限 下面,介绍一种通过重载

  • C++11 强类型枚举相关总结

    枚举就是定义一个类别,并且穷举统一类别下的个体以供代码使用. C++98 枚举存在的缺陷: 无论是具名枚举的名字还是枚举类型中的成员,都是全局范围的,其作用域是全局的. 如果在不同的枚举中定义了相同的枚举成员,则会出现重复声明(redeclaration)错误. enum PUBLIC_COLOR { RED, YELLOW, GREEN }; enum PRIVATE_COLOR { RED, BLACK, PURPLE }; // RED 重定义 enum PUBLIC_COLOR { WH

  • 详解C++11 变参模板

    1.概述 变参模板(variadic template)是C++11新增的最强大的特性之一,它对参数进行了高度泛化,它能表示0到任意个数.任意类型的参数.相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进.然而由于可变模版参数比较抽象,使用起来需要一定的技巧,掌握也存在一定的难度. 2.可变模版参数的展开 可变模板参数和普通模板参数的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号"-"

  • 详解C++11中的线程锁和条件变量

    线程 std::thread类, 位于<thread>头文件,实现了线程操作.std::thread可以和普通函数和 lambda 表达式搭配使用.它还允许向线程的执行函数传递任意多参数. #include <thread> void func() { // do some work } int main() { std::thread t(func); t.join(); return 0; } 上面的例子中,t是一个线程实例,函数func()在该线程运行.调用join()函数是

  • 详解C++11的std::addressof源码解析

    目录 1.源码准备 2.std::addressof简介 3.std::addressof源码解析 4.总结 1.源码准备 本文是基于gcc-4.9.0的源代码进行分析,std::addressof是C++11才加入标准的,所以低版本的gcc源码是没有这个的,建议选择4.9.0或更新的版本去学习,不同版本的gcc源码差异应该不小,但是原理和设计思想的一样的,下面给出源码下载地址 http://ftp.gnu.org/gnu/gcc 2.std::addressof简介 std::addresso

  • 详解C++11中模板的优化问题

    1. 模板的右尖括号 在泛型编程中,模板实例化有一个非常繁琐的地方,那就是连续的两个右尖括号(>>)会被编译器解析成右移操作符,而不是模板参数表的结束.我们先来看一段关于容器遍历的代码,在创建的类模板 Base 中提供了遍历容器的操作函数 traversal(): // test.cpp #include <iostream> #include <vector> using namespace std; template <typename T> class

随机推荐