我听到一些人建议在c++中使用枚举类,因为它们的类型安全。

但这到底是什么意思呢?


当前回答

与普通枚举相比,使用枚举类的基本优点是您可以为两个不同的枚举使用相同的枚举变量,并且仍然可以解析它们(这已经被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 };

对于基本枚举,编译器将无法区分红色是指下面语句中的类型Color1还是Color2。

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

其他回答

值得注意的是,在这些答案之上,c++ 20解决了枚举类的一个问题:冗长。想象一个假设的枚举类Color。

void foo(Color c)
  switch (c) {
    case Color::Red: ...;
    case Color::Green: ...;
    case Color::Blue: ...;
    // etc
  }
}

与普通枚举变体相比,这是详细的,其中名称在全局作用域中,因此不需要用Color::作为前缀。

然而,在c++ 20中,我们可以使用using enum将枚举中的所有名称引入到当前作用域,从而解决了这个问题。

void foo(Color c)
  using enum Color;
  switch (c) {
    case Red: ...;
    case Green: ...;
    case Blue: ...;
    // etc
  }
}

所以现在,没有理由不使用枚举类。

因为,正如在其他答案中所说,类enum不能隐式转换为int/bool,这也有助于避免有bug的代码,如:

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

枚举用于表示一组整数值。

枚举后面的class关键字指定枚举是强类型的,并且枚举数是有作用域的。这样枚举类可以防止意外误用常量。

例如:

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

与普通枚举相比,使用枚举类的基本优点是您可以为两个不同的枚举使用相同的枚举变量,并且仍然可以解析它们(这已经被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 };

对于基本枚举,编译器将无法区分红色是指下面语句中的类型Color1还是Color2。

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

c++ 11常见问题包括以下几点:

传统枚举隐式转换为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;
}