690

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

But what does that really mean?

Community
  • 1
  • 1
Oleksiy
  • 37,477
  • 22
  • 74
  • 122
  • 114
    When somebody claims that some programming construct is "evil" they are trying to discourage you from thinking for yourself. – Pete Becker Aug 20 '13 at 13:44
  • 5
    @NicolBolas: This is more of a rethorical question to provide a FAQ answer (whether this is truly *Frequenty* asked is a different story). – David Rodríguez - dribeas Aug 20 '13 at 13:51
  • 1
    @David, there's a discussion whether this should be an FAQ or not going on which starts [here](http://chat.stackoverflow.com/transcript/message/11355349#11355349). Input welcome. – sbi Aug 21 '13 at 09:21
  • 40
    @PeteBecker Sometimes they are merely trying to *protect* you from yourself. – piccy May 11 '18 at 16:15
  • 1
    https://www.geeksforgeeks.org/enum-classes-in-c-and-their-advantage-over-enum-datatype/ This is also a good place to understand `enum` vs `enum class`. – raz Apr 20 '20 at 15:53
  • Two words: "namespace pollution" – wcochran Apr 09 '21 at 22:04
  • @wcochran You might like the [namespace enum](https://stackoverflow.com/questions/482745/namespaces-for-enum-types-best-practices) technique – bobobobo Dec 11 '21 at 18:29

9 Answers9

769

C++ has two kinds of enum:

  1. enum classes
  2. Plain enums

Here are a couple of examples on how to declare them:

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

What is the difference between the two?

  • enum classes - enumerator names are local to the enum and their values do not implicitly convert to other types (like another enum or int)

  • Plain enums - where enumerator names are in the same scope as the enum and their values implicitly convert to integers and other types

Example:

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;

}

Conclusion:

enum classes should be preferred because they cause fewer surprises that could potentially lead to bugs.

Oleksiy
  • 37,477
  • 22
  • 74
  • 122
  • 12
    Good example... is there a way to combine the type safety of the class version with the namespace promotion of the enum version? That is, if I have a class `A` with state, and I create an `enum class State { online, offline };` as a child of class `A`, I'd like to do `state == online` checks inside of `A` instead of `state == State::online`... is that possible? – mark Aug 20 '13 at 13:48
  • 53
    Nope. The namespace promotion is a Bad Thing™ and half the justification for `enum class` was to eliminate it. – Puppy Aug 20 '13 at 13:59
  • 26
    In C++11, you can use explicitly typed enums too, like enum Animal: unsigned int {dog, deer, cat, bird} – Blasius Secundus Aug 20 '13 at 14:16
  • Note: on top of this, we now have the *explicit underlying type* available for both `enum`, which allows forward-declaration and stricter control about class layout :) – Matthieu M. Aug 20 '13 at 14:43
  • 3
    Why is "Color color = Color::red" an example of "bad use of of plain enums"? Is it bad simply because it is using "plain enums" or something else? – chux - Reinstate Monica Aug 20 '13 at 19:42
  • 1
    @chux The entire block is why plain enums are bad, not just the first statement. – Cat Plus Plus Aug 21 '13 at 18:47
  • 8
    @Cat Plus Plus I understand that @Oleksiy says it is bad. My question was not if Oleksiy thought it was bad. My question was a request to detail _what_ is bad about it. Specifically, _why_ does Oleksiy, for example, consider bad about `Color color = Color::red`. – chux - Reinstate Monica Aug 21 '13 at 18:56
  • 3
    @chux: Ugh, again, look at the code between this comment and " examples of good use of enum classes (safe)", not just the first statement. – Cat Plus Plus Aug 21 '13 at 19:08
  • 11
    @Cat Plus Plus So the example's _bad_ does not occur until the `if (color == Card::red_card)` line, 4 lines later than the comment (which I see now applies to the first half of the block.) 2 lines of the block gives the _bad_ examples. The first 3 lines are not a problem. The "entire block is why plain enums are bad" threw me as I thought you meant something was wrong with those too. I see now, it is just a set-up. In any case, thanks for the feedback. – chux - Reinstate Monica Aug 21 '13 at 19:32
  • 1
    @Oleksiy You give examples of how to use the different `enum` types. But why, WHY don't you explain *exactly* (as stated in the question) *why* this is *bad* and that is *good*. – TobiMcNamobi Oct 12 '15 at 09:22
  • 2
    Another benefit not mentioned is that enum class can be forward declared. – user2672165 Apr 04 '18 at 18:29
  • "_because they cause fewer surprises that could potentially lead to bugs._" I don't get it. What was the big surprise in this code? – Mathematician Oct 30 '18 at 13:43
  • 1
    I am really surprised that the `using` keyword hasn't been adjusted in the standard to allow for `using enum class Foo;` to promote all members of Foo to the current scope... Wouldn't that be super useful in, say, a tokenizer function? – Jimmio92 Aug 23 '19 at 15:43
  • Printing `cout << "bad" << endl;` for the last four `if` statements does not make sense. – Second Person Shooter Aug 10 '21 at 13:03
  • @TobiMcNamobi @Mathematician From the standpoint of type safetiness, which is what the original question was about, `color == Card::red_card` allows the comparison of a `Color` and a `Card` (this is "bad" if one considers that Colors shouldn't be comparable with Cards). – MTV Jan 05 '23 at 08:23
320

From Bjarne Stroustrup's C++11 FAQ:

The enum classes ("new enums", "strong enums") address three problems with traditional C++ enumerations:

  • conventional enums implicitly convert to int, causing errors when someone does not want an enumeration to act as an integer.
  • conventional enums export their enumerators to the surrounding scope, causing name clashes.
  • the underlying type of an enum cannot be specified, causing confusion, compatibility problems, and makes forward declaration impossible.

The new enums are "enum class" because they combine aspects of traditional enumerations (names values) with aspects of classes (scoped members and absence of conversions).

So, as mentioned by other users, the "strong enums" would make the code safer.

The underlying type of a "classic" enum shall be an integer type large enough to fit all the values of the enum; this is usually an int. Also each enumerated type shall be compatible with char or a signed/unsigned integer type.

This is a wide description of what an enum underlying type must be, so each compiler will take decisions on its own about the underlying type of the classic enum and sometimes the result could be surprising.

For example, I've seen code like this a bunch of times:

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

In the code above, some naive coder is thinking that the compiler will store the E_MY_FAVOURITE_FRUITS values into an unsigned 8bit type... but there's no warranty about it: the compiler may choose unsigned char or int or short, any of those types are large enough to fit all the values seen in the enum. Adding the field E_MY_FAVOURITE_FRUITS_FORCE8 is a burden and doesn't forces the compiler to make any kind of choice about the underlying type of the enum.

If there's some piece of code that rely on the type size and/or assumes that E_MY_FAVOURITE_FRUITS would be of some width (e.g: serialization routines) this code could behave in some weird ways depending on the compiler thoughts.

And to make matters worse, if some workmate adds carelessly a new value to our enum:

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

The compiler doesn't complain about it! It just resizes the type to fit all the values of the enum (assuming that the compiler were using the smallest type possible, which is an assumption that we cannot do). This simple and careless addition to the enum could subtlety break related code.

Since C++11 is possible to specify the underlying type for enum and enum class (thanks rdb) so this issue is neatly addressed:

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

Specifying the underlying type if a field have an expression out of the range of this type the compiler will complain instead of changing the underlying type.

I think that this is a good safety improvement.

So Why is enum class preferred over plain enum?, if we can choose the underlying type for scoped(enum class) and unscoped (enum) enums what else makes enum class a better choice?:

  • They don't convert implicitly to int.
  • They don't pollute the surrounding namespace.
  • They can be forward-declared.
PaperBirdMaster
  • 12,806
  • 9
  • 48
  • 94
  • 1
    I suppose we can restrict enum base type in for regular enums as well, so long as we have C++11 – Sagar Padhye Dec 28 '15 at 11:35
  • 23
    Sorry, but this answer is wrong. "enum class" has nothing to do with the ability to specify the type. That's an independent feature that exists both for regular enums and for enum classes. – rdb May 06 '16 at 11:01
  • 1
    @rdb maybe I'm wrong and I should fix my answer, how can I specify the underlying type for a traditional `enum`? BTW, in the [FAQ page linked](http://www.stroustrup.com/C++11FAQ.html#enum) you can read this: "*the underlying type of an **enum** cannot be specified, causing confusion, compatibility problems, and makes forward declaration impossible*". – PaperBirdMaster May 06 '16 at 12:33
  • Just like this: `enum A : unsigned char {...}`. The article describes pre-C++11, in which this was not possible. But this has nothing to do with enum classes. – rdb May 06 '16 at 13:00
  • 23
    This is the deal: * Enum classes are a new feature in C++11. * Typed enums are a new feature in C++11. These are two separate unrelated new features in C++11. You can use both, or you can use either, or neither. – rdb May 06 '16 at 13:20
  • 3
    I think that Alex Allain provides the most complete _simple_ explanation I have yet seen in this blog at [http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html]. Traditional **enum** was good for using names instead of integer values and avoiding using preprocessor #defines, which was a Good Thing - it added clarity. **enum class** removes the concept of a numeric value of the enumerator, and introduces scope and strong typing which increases (well, _can_ increase :-) program correctness. It moves you one step closer to thinking object oriented. – Jon Spencer Jan 06 '17 at 21:10
  • wow this is the best of both worlds; typesafe by default, but not binary incompatible with C. – Dmytro Apr 01 '18 at 02:19
  • 1
    However, I believe you have to define the bitwise operations yourself which adds a lot of boilerplate code. – user2672165 Apr 04 '18 at 18:27
  • 2
    As an aside, it's always amusing when you're reviewing code and suddenly _One Piece_ happens. – Justin Time - Reinstate Monica Sep 25 '19 at 17:32
  • Hahah - yes this is a situation where you declare flags - If you have a "class Enum" you can't do if(((Type)in & (Type::a | Type::b)+ == 0) { // neither flag set in a } – peterk Oct 04 '22 at 23:47
63

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??)
Saksham
  • 9,037
  • 7
  • 45
  • 73
  • 2
    @Oleksiy Ohh I didn't read your question properly. Consider is as an add-on for those who didn't know. – Saksham Aug 20 '13 at 13:12
  • 1
    of course, you'd write `enum { COLOR1_RED, COLOR1_GREE, COLOR1_BLUE }`, easily obviating namespace issues. The namespace argument is the one of the three mentioned here that I don't buy at all. – Jo So Oct 26 '18 at 18:48
  • 3
    @Jo So That solution is an unnecessary workaround. Enum: `enum Color1 { COLOR1_RED, COLOR1_GREEN, COLOR1_BLUE }` is comparable to Enum class: `enum class Color1 { RED, GREEN, BLUE }`. Accessing is similar: `COLOR1_RED` vs `Color1::RED`, but the Enum version requires you type "COLOR1" in each value, which gives more room for typos, which the namespace behaviour of an enum class avoids. – cdgraham Mar 05 '19 at 19:15
  • `which gives more room for typos`. Sorry but that is the worst hair-pulling argument I've heard in 2019. Time to practice a bit and get a feel for what matters. I guarantee you that humans are not as fast at resolving identifiers as compilers. So better don't challenge them to do it. *That* is a convincing argument. – Jo So Mar 06 '19 at 22:13
  • To clarify: It's hair pulling because if you have a typo in the prefix, chances are 99.99% that the compiler will catch it. For the remaining 0.01%, chances are that the programmer will notice since the program misbehaves. – Jo So Mar 06 '19 at 22:15
  • @cdgraham Now consider: If you write `RED`, `GREEN` and so on (using enum class), isn't the chance much higher that the automated resolution will resolve to the `enum Banana` enum instead of the `Color1` enum? Please, learn to be critical and try to be objective. – Jo So Mar 06 '19 at 22:17
  • 4
    Please use [constructive criticism](https://stackoverflow.com/help/privileges/comment). When I say more room for typos, I mean when you originally define the values of `enum Color1`, which a compiler can't catch since it would likely still be a 'valid' name. If I write `RED`, `GREEN` and so on using an enum class, than it can't resolve to `enum Banana` because it requires you specify `Color1::RED` in order to access the value (the namespace argument). There are still good times to use `enum`, but the namespace behavior of an `enum class` can often be very beneficial. – cdgraham Mar 07 '19 at 13:50
25

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
eigenchris
  • 5,791
  • 2
  • 21
  • 30
Alok151290
  • 251
  • 3
  • 3
20

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.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
themeeman
  • 429
  • 5
  • 6
11
  1. do not implicitly convert to int
  2. can choose which type underlie
  3. ENUM namespace to avoid polluting happen
  4. Compared with normal class, can be declared forward, but do not have methods
Qinsheng Zhang
  • 1,113
  • 1
  • 11
  • 19
9

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;
}
Swapnil
  • 1,424
  • 2
  • 19
  • 30
  • 1
    C++11 allows "non-class" enums to be *typed* as well. The namespace pollution issues, etc, still exist. Take a look at relevant answers that existed a long time before this one.. – user2864740 Feb 17 '20 at 18:30
5

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
};
Miro Kropacek
  • 2,742
  • 4
  • 26
  • 41
3

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
Arnaud
  • 536
  • 5
  • 13
  • 3
    To complete my previous comment, note that gcc now has a warning called -Wint-in-bool-context which will catch exactly this kind of errors. – Arnaud Jan 04 '19 at 13:42