Why is enum class preferred over plain enum?

前端 未结 9 2230
灰色年华
灰色年华 2020-11-22 10:12

I heard a few people recommending to use enum classes in C++ because of their type safety.

But what does that really mean?

相关标签:
9条回答
  • 2020-11-22 10:35

    Because, as said in other answers, class enum are not implicitly convertible to int/bool, it also helps to avoid buggy code like:

    enum MyEnum {
      Value1,
      Value2,
    };
    ...
    if (var == Value1 || Value2) // Should be "var == Value2" no error/warning
    
    0 讨论(0)
  • 2020-11-22 10:37

    The basic advantage of using enum class over normal enums is that you may have same enum variables for 2 different enums and still can resolve them(which has been mentioned as type safe by OP)

    For eg:

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

    As for the basic enums, compiler will not be able to distinguish whether red is refering to the type Color1 or Color2 as in hte below statement.

    enum Color1 { red, green, blue };   
    enum Color2 { red, green, blue };
    int x = red;    //Compile time error(which red are you refering to??)
    
    0 讨论(0)
  • 2020-11-22 10:39

    Enumerations are used to represent a set of integer values.

    The class keyword after the enum specifies that the enumeration is strongly typed and its enumerators are scoped. This way enum classes prevents accidental misuse of constants.

    For Example:

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

    Here we can not mix Animal and Pets values.

    Animal a = Dog;       // Error: which DOG?    
    Animal a = Pets::Dog  // Pets::Dog is not an Animal
    
    0 讨论(0)
  • 2020-11-22 10:39

    It's worth noting, on top of these other answers, that C++20 solves one of the problems that enum class has: verbosity. Imagining a hypothetical enum class, Color.

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

    This is verbose compared to the plain enum variation, where the names are in the global scope and therefore don't need to be prefixed with Color::.

    However, in C++20 we can use using enum to introduce all of the names in an enum to the current scope, solving the problem.

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

    So now, there is no reason not to use enum class.

    0 讨论(0)
  • 2020-11-22 10:41

    One thing that hasn't been explicitly mentioned - the scope feature gives you an option to have the same name for an enum and class method. For instance:

    class Test
    {
    public:
       // these call ProcessCommand() internally
       void TakeSnapshot();
       void RestoreSnapshot();
    private:
       enum class Command // wouldn't be possible without 'class'
       {
            TakeSnapshot,
            RestoreSnapshot
       };
       void ProcessCommand(Command cmd); // signal the other thread or whatever
    };
    
    0 讨论(0)
  • 2020-11-22 10:42

    C++11 FAQ mentions below points:

    conventional enums implicitly convert to int, causing errors when someone does not want an enumeration to act as an integer.

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

    conventional enums export their enumerators to the surrounding scope, causing name clashes.

    // 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
    };
    

    The underlying type of an enum cannot be specified, causing confusion, compatibility problems, and makes forward declaration impossible.

    // 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;
    }
    
    0 讨论(0)
提交回复
热议问题