为什么枚举类优于普通枚举?为什么枚举类优于普通枚举?(Why is enum class prefe

2019-05-08 18:06发布

我听说有几个人建议要在C枚举 ++,因为它们的类型安全的。

但到底是什么究竟意味着什么?

Answer 1:

C ++有两种enum

  1. enum class胚胎干
  2. 平原enum小号

这里有几个例子如何声明它们:

 enum class Color { red, green, blue }; // enum class
 enum Animal { dog, cat, bird, human }; // plain enum 

什么是两者之间的区别?

  • enum class ES -枚举名是本地的枚举和它们的值隐式转换为其他类型(如另一个enumint

  • 平原enum秒-这里枚举的名字都在同一范围内枚举和它们的值隐式转换为整数和其他类型

例:

enum Color { red, green, blue };                    // plain enum 
enum Card { red_card, green_card, yellow_card };    // another plain enum 
enum class Animal { dog, deer, cat, bird, human };  // enum class
enum class Mammal { kangaroo, deer, human };        // another enum class

void fun() {

    // examples of bad use of plain enums:
    Color color = Color::red;
    Card card = Card::green_card;

    int num = color;    // no problem

    if (color == Card::red_card) // no problem (bad)
        cout << "bad" << endl;

    if (card == Color::green)   // no problem (bad)
        cout << "bad" << endl;

    // examples of good use of enum classes (safe)
    Animal a = Animal::deer;
    Mammal m = Mammal::deer;

    int num2 = a;   // error
    if (m == a)         // error (good)
        cout << "bad" << endl;

    if (a == Mammal::deer) // error (good)
        cout << "bad" << endl;

}

结论:

enum class ES应该是首选,因为它们会导致更少的惊喜,可能潜在地导致错误。



Answer 2:

从Bjarne的Stroustrup的C ++ 11 FAQ :

enum class ES(“新枚举”,“强枚举”)解决三个问题,与传统的C ++枚举:

  • 传统枚举隐式转换为int,导致错误,当有人不希望一个枚举作为一个整数。
  • 传统枚举的统计员导出到周围的范围,造成名称冲突。
  • 基础类型的的enum不能被指定,从而导致混乱,相容性问题,并使得前向声明是不可能的。

新的枚举是“枚举类”,因为它们结合了传统枚举(名称值)方面与(作用域成员缺乏转换和)类方面。

因此,当其他用户提到,“强枚举”会使代码更安全。

基础类型“经典”的enum应该是整型类型足够大,以适应的所有值enum ; 这通常是一个int 。 同样,每个枚举类型应与兼容char或符号/无符号整数类型。

这是一个什么样的广泛描述enum基础类型必须是,这样每个编译器将决定他自己对基本类型的经典之作enum ,有时其结果可能是惊人的。

例如,我看到了这样一串代码的时间:

enum E_MY_FAVOURITE_FRUITS
{
    E_APPLE      = 0x01,
    E_WATERMELON = 0x02,
    E_COCONUT    = 0x04,
    E_STRAWBERRY = 0x08,
    E_CHERRY     = 0x10,
    E_PINEAPPLE  = 0x20,
    E_BANANA     = 0x40,
    E_MANGO      = 0x80,
    E_MY_FAVOURITE_FRUITS_FORCE8 = 0xFF // 'Force' 8bits, how can you tell?
};

在上面的代码中,一些天真的编码器认为该编译器将存储E_MY_FAVOURITE_FRUITS值转换成一个无符号的8位类型......但有没有关于它的保修:编译器可能选择unsigned charintshort ,这些类型的任何大足以容纳在看到的所有值enum 。 添加场E_MY_FAVOURITE_FRUITS_FORCE8是一种负担,不强制编译器做出什么样的选择有关基础类型的enum

如果有一些一段代码依赖于类型的大小和/或假定E_MY_FAVOURITE_FRUITS将一些宽度(如:序列化程序)这个代码可以在行为取决于编译器的思想有些怪异的方式。

而更糟糕的是,如果有些同事不小心添加了一个新的价值,我们的enum

    E_DEVIL_FRUIT  = 0x100, // New fruit, with value greater than 8bits

编译器不抱怨! 它只是调整大小以适应所有的值类型enum (假设编译器使用最小的类型可能的,这是我们不能做的假设)。 这个简单而粗心大意除了enum可能精妙突破相关的代码。

由于C ++ 11可以指定基本类型enumenum class (感谢RDB ),所以这个问题是解决整齐:

enum class E_MY_FAVOURITE_FRUITS : unsigned char
{
    E_APPLE        = 0x01,
    E_WATERMELON   = 0x02,
    E_COCONUT      = 0x04,
    E_STRAWBERRY   = 0x08,
    E_CHERRY       = 0x10,
    E_PINEAPPLE    = 0x20,
    E_BANANA       = 0x40,
    E_MANGO        = 0x80,
    E_DEVIL_FRUIT  = 0x100, // Warning!: constant value truncated
};

指定的基础类型如果字段具有表达出该类型的范围内的编译器会抱怨而不是改变的基础类型。

我认为这是一个良好的安全性的提高。

那么, 为什么是枚举类优于普通枚举? 如果我们可以选择的范围的(底层类型enum class )和无范围( enum )枚举还有什么让enum class是更好的选择?:

  • 他们不转换隐式int
  • 他们不污染周围的命名空间。
  • 它们可以是前置声明。


Answer 3:

使用枚举类超过正常枚举的基本优点是,你可能具有相同的枚举变量2个不同的枚举而且还可以解决这些问题(已规定由OP 类型安全

对于例如:

enum class Color1 { red, green, blue };    //this will compile
enum class Color2 { red, green, blue };

enum Color1 { red, green, blue };    //this will not compile 
enum Color2 { red, green, blue };

至于基本的枚举,编译器将无法区分是否red是指的类型Color1Color2如下声明HTE。

enum Color1 { red, green, blue };   
enum Color2 { red, green, blue };
int x = red;    //Compile time error(which red are you refering to??)


Answer 4:

枚举是用来表示一组的整数值。

class的关键字后enum指定枚举是强类型和枚举的作用域。 这样一来enum类常量防止意外的误操作。

例如:

enum class Animal{Dog, Cat, Tiger};
enum class Pets{Dog, Parrot};

在这里,我们不能混用动物和宠物的值。

Animal a = Dog;       // Error: which DOG?    
Animal a = Pets::Dog  // Pets::Dog is not an Animal


Answer 5:

C ++ 11 FAQ提到以下几点:

传统枚举隐式转换为int,导致错误,当有人不希望一个枚举作为一个整数。

enum color
{
    Red,
    Green,
    Yellow
};

enum class NewColor
{
    Red_1,
    Green_1,
    Yellow_1
};

int main()
{
    //! Implicit conversion is possible
    int i = Red;

    //! Need enum class name followed by access specifier. Ex: NewColor::Red_1
    int j = Red_1; // error C2065: 'Red_1': undeclared identifier

    //! Implicit converison is not possible. Solution Ex: int k = (int)NewColor::Red_1;
    int k = NewColor::Red_1; // error C2440: 'initializing': cannot convert from 'NewColor' to 'int'

    return 0;
}

传统枚举的统计员出口到周边范围,造成名称冲突。

// Header.h

enum vehicle
{
    Car,
    Bus,
    Bike,
    Autorickshow
};

enum FourWheeler
{
    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
};

基础类型枚举的不能被指定,从而导致混乱,相容性问题,并使得前向声明是不可能的。

// Header1.h
#include <iostream>

using namespace std;

enum class Port : unsigned char; // Forward declare

class MyClass
{
public:
    void PrintPort(enum class Port p);
};

void MyClass::PrintPort(enum class Port p)
{
    cout << (int)p << endl;
}

// Header.h
enum class Port : unsigned char // Declare enum type explicitly
{
    PORT_1 = 0x01,
    PORT_2 = 0x02,
    PORT_3 = 0x04
};

// Source.cpp
#include "Header1.h"
#include "Header.h"

using namespace std;
int main()
{
    MyClass m;
    m.PrintPort(Port::PORT_1);

    return 0;
}


Answer 6:

因为,在其他的答案说,类枚举不是隐式转换为int /布尔,这也有助于避免类似bug的代码:

enum MyEnum {
  Value1,
  Value2,
};
...
if (var == Value1 || Value2) // Should be "var == Value2" no error/warning


文章来源: Why is enum class preferred over plain enum?