【问题标题】:Enum vs Strongly typed enum枚举与强类型枚举
【发布时间】:2012-09-16 20:37:14
【问题描述】:

我是 C++ 编程的初学者。

今天遇到一个新话题:强类型enum。我已经对其进行了一些研究,但直到现在我无法找出我们为什么需要它以及它的用途是什么?

例如,如果我们有:

enum xyz{a, b, c};
/*a = 0, b = 1, c = 2, (Typical C format)*/

为什么要写:

enum class xyz{a, b, c};

我们想在这里做什么? 我最重要的疑问是如何使用它。 能否举个小例子,让我明白。

【问题讨论】:

    标签: c++ c++11 enums


    【解决方案1】:

    好的,第一个例子:旧式枚举没有自己的范围:

    enum Animals {Bear, Cat, Chicken};
    enum Birds {Eagle, Duck, Chicken}; // error! Chicken has already been declared!
    
    enum class Fruits { Apple, Pear, Orange };
    enum class Colours { Blue, White, Orange }; // no problem!
    

    其次,它们隐式转换为整数类型,这可能会导致奇怪的行为:

    bool b = Bear && Duck; // what?
    

    最后,可以指定C++11枚举的底层整数类型:

    enum class Foo : char { A, B, C};
    

    以前,未指定底层类型,这可能会导致平台之间的兼容性问题。 编辑 在 cmets 中已经指出,您还可以在 C++11 中指定“旧式”枚举的底层整数类型。

    【讨论】:

    • 我们是否需要声明/定义enum class Coloursenum class Fruits。因为当我在 VS 2010 中编写代码时,它会在 class 下抛出错误 "expects a defination or a tag name"
    • 另外:对于 C++ 11 中的“普通”枚举和 C++ 98 中的默认基础类型未定义
    • 另外:如果指定了基础类型,您可以对 enum-s 进行前向声明。对于 C++11、C++98 中的普通枚举是不允许的。微软编译器允许你做 enum 的前向声明,但它只是 MS 扩展,它不是标准的(例如 gcc 不允许它)所以现在这样的事情是合法的: enum ForwardDeclare: std :: uint8_t;
    • 我们能否有范围枚举也隐式转换为整数类型?
    • @S.S.Anne 不,隐式转换反而违背了强类型枚举的目的。定义一个模板函数来显式执行转换;使用 std::underlying_type::type 提取类型。
    【解决方案2】:

    this IBM page 有一篇关于枚举的好文章,非常详细而且写得很好。简而言之,这里有一些要点:

    作用域枚举解决了常规枚举的大部分限制:完整的类型安全、定义良好的底层类型、作用域问题和前向声明。

    • 您可以通过禁止范围内枚举到其他类型的所有隐式转换来获得类型安全。
    • 您获得了一个新范围,并且枚举不再位于封闭范围内,从而避免了名称冲突。
    • 范围枚举使您能够指定枚举的基础类型,对于范围枚举,如果您选择不指定它,则默认为 int。
    • 任何具有固定基础类型的枚举都可以前向声明。

    【讨论】:

    • 第三点和第四点并不特定于范围枚举;您可以指定任何枚举的基础类型。
    • 任何人有一个链接到一个较少损坏的 PDF 版本?其中的代码示例不会在我的任何 PDF 查看器中呈现,这给我留下了很多想象空间。
    【解决方案3】:

    enum class 的值实际上是 enum class 类型,而不是 C 枚举的 underlying_type

    enum xyz { a, b, c};
    enum class xyz_c { d, f, e };
    
    void f(xyz x)
    {
    }
    
    void f_c(xyz_c x)
    {
    }
    
    // OK.
    f(0);
    // OK for C++03 and C++11.
    f(a);
    // OK with C++11.
    f(xyz::a);
    // ERROR.
    f_c(0);
    // OK.
    f_c(xyz_c::d);
    

    【讨论】:

      【解决方案4】:

      枚举类(“新枚举”、“强枚举”)解决了传统 C++ 枚举的三个问题:

      1. 传统的enums 隐式转换为int,当有人不希望枚举充当整数时会导致错误。
      2. 传统的enums 将其枚举数导出到周围的作用域,导致名称冲突。
      3. 无法指定enum 的底层类型,导致混淆、兼容性问题,并且无法进行前向声明。

      enum class ("strong enums") 是强类型和作用域的:

      enum Alert { green, yellow, orange, red }; // traditional enum
      
      enum class Color { red, blue };   // scoped and strongly typed enum
                                        // no export of enumerator names into enclosing scope
                                        // no implicit conversion to int
      enum class TrafficLight { red, yellow, green };
      
      Alert a = 7;              // error (as ever in C++)
      Color c = 7;              // error: no int->Color conversion
      
      int a2 = red;             // ok: Alert->int conversion
      int a3 = Alert::red;      // error in C++98; ok in C++11
      int a4 = blue;            // error: blue not in scope
      int a5 = Color::blue;     // error: not Color->int conversion
      
      Color a6 = Color::blue;   // ok
      

      如图所示,传统枚举照常工作,但您现在可以选择使用枚举名称进行限定。

      新的枚举是“枚举类”,因为它们将传统枚举(名称值)的各个方面与类的各个方面(作用域成员和不存在转换)结合在一起。

      能够指定底层类型允许更简单的互操作性和有保证的枚举大小:

      enum class Color : char { red, blue };  // compact representation
      
      enum class TrafficLight { red, yellow, green };  // by default, the underlying type is int
      
      enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U };   // how big is an E?
                                                       // (whatever the old rules say;
                                                       // i.e. "implementation defined")
      
      enum EE : unsigned long { EE1 = 1, EE2 = 2, EEbig = 0xFFFFFFF0U };   // now we can be specific
      

      它还支持枚举的前向声明:

      enum class Color_code : char;     // (forward) declaration
      void foobar(Color_code* p);       // use of forward declaration
      // ...
      enum class Color_code : char { red, yellow, green, blue }; // definition
      

      基础类型必须是有符号或无符号整数类型之一;默认为int

      在标准库中,enum 类用于:

      1. 映射系统特定错误代码:在<system_error>:enum class errc;
      2. 指针安全指示器:在<memory>:enum class pointer_safety { relaxed, preferred, strict };
      3. I/O 流错误:在<iosfwd>enum class io_errc { stream = 1 };
      4. 异步通信错误处理:在<future>:enum class future_errc { broken_promise, future_already_retrieved, promise_already_satisfied };

      其中有几个定义了运算符,例如==

      【讨论】:

        【解决方案5】:

        枚举范围

        枚举将其枚举数导出到周围范围。这有两个缺点。首先,如果在同一作用域中声明的不同枚举中的两个枚举数具有相同的名称,则会导致名称冲突;其次,不能使用具有完全限定名称的枚举器,包括枚举名称。

        enum ESet {a0, a, a1, b1, c3};
        enum EAlpha{a, b, c}
        
        select = ESet::a; // error
        select = a;       // is ambigious
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-01-17
          • 1970-01-01
          • 2023-02-21
          • 2018-10-18
          • 2017-01-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多