• 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 };
    
    • 1

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

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

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

    enum Color:int;
    
    • 1

    知识点位

    多个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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    默认从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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    未赋值的前项+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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    所有值都必须是整型常量

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

    元素作用域惟一

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

    enum Cars{Mahindra, Jeep, BMW};
    enum Luxury_Cars{BMW, Ferrari, Mercedes_Benz};
    int main(){
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    编译失败

    <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};
          |                           ^~~
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    1. enum struct|class 名字 : 类型 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... }
      声明底层类型为 类型 的scoped enum类型
    enum class Color:int { red=0, green = 20, blue=1 };
    
    • 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    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
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    编译错误

    <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
    • 2
    • 3
    • 4
    • 5
    • 6
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    枚举的大小

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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    运行结果如下:

     color  底层类型 char
     color2 底层类型 int
     color3 底层类型 non-int
     E      底层类型 non-int
     EE     底层类型 unsigned long
    
    • 1
    • 2
    • 3
    • 4
    • 5

    优选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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 指针安全指示器: 在 <memory>
    enum class pointer_safety {
         relaxed,
         preferred,
         strict
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    参考

    [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

  • 相关阅读:
    数据结构 | (二) List
    Myeclipse反编译插件(jad)的安装和使用
    测试15k薪资第1步 —— 自动化测试理论基础
    数据可视化:理论与技术
    基于超参数自动寻优的工控网络入侵检测
    【笔记】大话设计模式-567
    java数据结构与算法刷题-----LeetCode28:实现 strStr()
    vue3 canvas验证码和滑块拼图验证
    多校联测13 菜
    常见的C语言类型转换
  • 原文地址:https://blog.csdn.net/MMTS_yang/article/details/125547409