IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> Effective Modern C++[实践]->优先使用 scoped enums,而非 unscoped enums -> 正文阅读

[C++知识库]Effective Modern C++[实践]->优先使用 scoped enums,而非 unscoped enums

  • C++98 风格的枚举,现在称作为 unscoped enum
  • scoped enum 的枚举元素只在 enum 内可见, 元素只能强制转换成其他类型。
  • scoped enum unscoped enum 都可以指定潜在类型。 scoped enum 默认是 intunscoped enum 没有默认类型。
  • scoped enum 总是能前置声明,unscoped enum 只有指定了潜在类型, 才可以前置声明。

枚举

枚举(enumeration)是独立的类型,它的值被限制在一个取值范围内,它可以包含数个明确命名的常量(“枚举项(enumerator)”)。各常量的值是某个整型类型(称为该枚举的底层类型(underlying type))的值。

有两种截然不同的枚举:unscoped enum(以 枚举关键词 enum 声明)和scoped enum(以 枚举关键词 enum class 或 enum struct 声明)。

unscoped enum

简要说明

enum 名字(可选) { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , … }

enum Color { red, green, blue };

enum 名字(可选) : 类型 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , … } (C++11 起)

enum Color:int { red, green, blue };

enum 名字 : 类型 ; (C++11 起) 前置声明

enum Color:int;

知识点位

多个enum名称或元素可以有相同的值

下面是两个具有相似值的enum元素的示例。

#include <iostream>
using namespace std;

enum Cars{Jeep = 1, BMW = 0, Mercedes_Benz = 0};
int main(){
  	cout<<"Jeep= "<<Jeep<<"   BMW= "<<BMW<<"  Benz= "<<Mercedes_Benz;
    return 0;
}

默认从0开始

如果不给enum元素赋自定义值,编译器会给它们赋从0开始的默认值。

#include <iostream>
using namespace std;

enum Months{January, February, March, April, May, June, July, August, September, October, November, December};
int main(){
    enum Months m = May;
    cout<< "以月份为单位的五月的值为 "<< m;
    return 0;
}

未赋值的前项+1

可以以任何顺序为enum中的任何元素提供值。所有未赋值的元素将获得先前的+ 1值。

#include <iostream>
using namespace std;

enum weekdays {
    Sunday,
    Monday = 2,
    Tuesday,
    Wednesday = 6,
    Thursday,
    Friday = 9,
    Saturday = 12
};
int main(){
    cout << "Sunday 值=" << Sunday << "  Tuesday的值 = " << Tuesday;//Sunday 值=0  Tuesday的值 = 3
    return 0;
}

所有值都必须是整型常量

赋给enum元素的所有值都必须是整型常量。例如,它们应该在最小和最大可能整数的范围内。

元素作用域惟一

所有枚举元素或常量都应该具有惟一的作用域。这意味着一个元素不能成为同一程序中两个不同枚举的一部分,因为它将在编译期间失败。

enum Cars{Mahindra, Jeep, BMW};
enum Luxury_Cars{BMW, Ferrari, Mercedes_Benz};
int main(){
    return 0;
}

编译失败

<source>:2:18: error: 'BMW' conflicts with a previous declaration
    2 | enum Luxury_Cars{BMW, Ferrari, Mercedes_Benz};
      |                  ^~~
<source>:1:27: note: previous declaration 'Cars BMW'
    1 | enum Cars{Mahindra, Jeep, BMW};
      |                           ^~~

scoped enum

简要说明

  1. enum struct|class 名字 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... }
    声明底层类型为 int 的scoped enum类型(关键词 class 与 struct 完全等价)

示例

#include <iostream>

using namespace std;

enum class Color { red, green = 20, blue };

int main() {
    Color r = Color::blue;
    switch (r) {
        case Color::red:
            std::cout << "红\n";
            break;
        case Color::green:
            std::cout << "绿\n";
            break;
        case Color::blue:
            std::cout << "蓝\n";
            break;
    }
    return 0;
}
  1. enum struct|class 名字 : 类型 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... }
    声明底层类型为 类型 的scoped enum类型
enum class Color:int { red=0, green = 20, blue=1 };
  1. enum struct|class 名字 ; 前置声明
    底层类型为 int 的scoped enum类型的不可见枚举声明
  1. enum struct|class 名字 : 类型 ; 前置声明
    底层类型为 类型 的scoped enum类型的不可见枚举声明

解决了三个问题

enum classes (new enumsstrong enums) 解决了传统c++枚举的三个问题:

  1. 常规枚举隐式转换为int,当有人不希望枚举充当整数时,会导致错误。

unscoped enum类型中的枚举量可以隐式的转换为整型类别(并能够转换为浮点类型),scoped enum类型中枚举量不能转换为其他类别。

enum color{
    Red,
    Green,
    Yellow
};
enum class NewColor{
    Red_1,
    Green_1,
    Yellow_1
};
int main()
{
    //! 隐式转换是可能的
    int i = Red;

    //! 需要enum类名后跟访问说明符。 Ex: NewColor::Red_1
    // int j = Red_1; // error C2065: 'Red_1': undeclared identifier

    //! 隐式转换是不可能的。Solution Ex: int k = (int)NewColor::Red_1;
    // int k = NewColor::Red_1; // error C2440: 'initializing': cannot convert from 'NewColor' to 'int'

    return 0;
}
  1. 传统枚举将枚举器超出到周围的作用域,导致名称冲突。
enum vehicle{
    Car,
    Bus,
    Bike,
    Autorickshow
};

enum FourWheeler{
    Car,        // car 已经在范围内被声明过,error C2365: 'Car': redefinition; previous definition was 'enumerator'
    SmallBus
};

enum class Editor{
    vim,
    eclipes,
    VisualStudio
};

enum class CppEditor{
    eclipes,       // No error of redefinitions
    VisualStudio,  // No error of redefinitions
    QtCreator
};

编译错误

<source>:11:5: error: 'Car' conflicts with a previous declaration
   11 |     Car,        // error C2365: 'Car': redefinition; previous definition was 'enumerator'
      |     ^~~
<source>:3:5: note: previous declaration 'vehicle Car'
    3 |     Car,
      |     ^~~
  1. 不能指定枚举的基础类型,这会导致混淆、兼容性问题,并导致无法前置声明。
    注:C++11后传统的枚举也可以被前置声明
#include <iostream>

using namespace std;

enum class Port : unsigned char; // 前置声明

class MyClass{
public:
    void PrintPort(enum class Port p);
};
void MyClass::PrintPort(enum class Port p){
    cout << (int)p << endl;
}
int main(){
    MyClass a;
    a.PrintPort(static_cast<Port>(1));
    return 0;
}

枚举的大小

每个枚举类型都拥有底层类型,它可以是

  1. 显式指定(scoped enumunscoped enum均可)
  2. 省略,该情况下对于有作用域枚举是 int ,或(对于无作用域枚举)是足以表示枚举所有值的实现定义的整数类型
#include <iostream>
#include <type_traits>

using namespace std;

enum class Color : char { red, blue };  

enum class Color2 {
    red,
    yellow,
    green
};  
enum  Color3 {
    red,
    yellow,
    green
}; 

enum E {
    E1 = 1,
    E2 = 2,
    Ebig = 0xFFFFFFF0U
}; 
enum EE : unsigned long {
    EE1 = 1,
    EE2 = 2,
    EEbig = 0xFFFFFFF0U
};
 
int main() {
 
  constexpr bool e1 = std::is_same_v< std::underlying_type_t<Color>, char >;
  constexpr bool e2 = std::is_same_v< std::underlying_type_t<Color2>, int >;
  constexpr bool e3 = std::is_same_v< std::underlying_type_t<Color3>, int >;
  constexpr bool e4 = std::is_same_v< std::underlying_type_t<E>, int >;
  constexpr bool e5 = std::is_same_v< std::underlying_type_t<EE>, unsigned long >;

  std::cout
    << " color  底层类型 " << (e1 ? "char" : "non-char") << '\n'
    << " color2 底层类型 " << (e2 ? "int" : "non-int") << '\n'
    << " color3 底层类型 " << (e3 ? "int" : "non-int") << '\n'
    << " E      底层类型 " << (e4 ? "int" : "non-int") << '\n'
    << " EE     底层类型 " << (e5 ? "unsigned long" : "unsigned long") << '\n'
    ;

    return 0;
}

运行结果如下:

 color  底层类型 char
 color2 底层类型 int
 color3 底层类型 non-int
 E      底层类型 non-int
 EE     底层类型 unsigned long

优选scope enum的三个理由

  1. 降低名字空间污染
  2. 枚举量的更强类别,从限定作用域的枚举类别到任何其他类别都不能隐士转换
  3. scope enum可以进行前置声明,而unscope enum需要进行简单处理才能进行前置声明。

标准库的枚举

在标准库中,使用enum类

  1. 对于映射系统特定的错误代码:在标准库头文件 <system_error>,如下:
enum class errc {
    address_family_not_supported,       // EAFNOSUPPORT
    address_in_use,                     // EADDRINUSE
    address_not_available,              // EADDRNOTAVAIL
    already_connected,                  // EISCONN
		...
    wrong_protocol_type,                // EPROTOTYPE
  };
  1. 指针安全指示器: 在 <memory>
enum class pointer_safety {
     relaxed,
     preferred,
     strict
};
  • pointer_safety::strict 只有安全派生的指针(指向以 new 分配对象或其子对象的指针)可解引用或解分配。垃圾收集器可能活跃。
  • pointer_safety::preferred 认为所有指针均合法且可以解引用或解分配。基于可达性的泄露检测器可能活跃。
  • pointer_safety::relaxed 认为所有指针均合法且可以解引用或解分配。
  1. 对于I/O流错误:位于<iosfwd>
    enum class io_errc;(C++11 起)

带作用域枚举 std::io_errc 定义 I/O 流报告于 std::ios_base::failure 异常对象的错误码。只要求一个错误码( std::io_errc::stream ),虽然实现可以定义额外的错误码。因为提供了std::is_error_code_enum的适当特化,故 std::io_errc 类型的值能隐式转换成 std::error_code

  1. 对于异步通信错误处理: 在<future>
enum class future_errc {

    broken_promise             = /* implementation-defined */,
    future_already_retrieved   = /* implementation-defined */,
    promise_already_satisfied  = /* implementation-defined */,
    no_state                   = /* implementation-defined */
};

参考

[1] Enum in C: Understanding The Concept of Enumeration
[2] Why is enum class preferred over plain enum?
[3] Enumeration declaration
[4] enum class – scoped and strongly typed enums
[5] Why am I getting this ‘enum’ is not a class or a namespace error?
[6] How to use enums in C++
[7] std::pointer_safety

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-04 22:37:15  更:2022-07-04 22:39:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/11 15:55:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码