Search

4.5 — Enumerated types

C++ contains quite a few built in data types. But these types aren’t always sufficient for the kinds of things we want to do. So C++ contains capabilities that allow programmers to create their own data types. These data types are called user-defined data types.

Perhaps the simplest user-defined data type is the enumerated type. An enumerated type (also called an enumeration) is a data type where every possible value is defined as a symbolic constant (called an enumerator). Enumerations are defined via the enum keyword. Let’s look at an example:

Defining an enumeration (or any user-defined data type) does not allocate any memory. When a variable of the enumerated type is defined (such as variable paint in the example above), memory is allocated for that variable at that time.

Note that each enumerator is separated by a comma, and the entire enumeration is ended with a semicolon.

Prior to C++11, a trailing comma after the last enumerator (e.g. after COLOR_MAGENTA) is not allowed (though many compilers accepted it anyway). However, starting with C++11, a trailing comma is allowed. Now that C++11 compilers are more prevalent, use of a trailing comma after the last element is generally considered acceptable.

Naming enums

Enum identifiers are often named starting with a capital letter, and the enumerators are often named using all caps. Because enumerators are placed into the same namespace as the enumeration, an enumerator name can’t be used in multiple enumerations within the same namespace:

Consequently, it’s common to prefix enumerators with a standard prefix like ANIMAL_ or COLOR_, both to prevent naming conflicts and for code documentation purposes.

Enumerator values

Each enumerator is automatically assigned an integer value based on its position in the enumeration list. By default, the first enumerator is assigned the integer value 0, and each subsequent enumerator has a value one greater than the previous enumerator:

The cout statement above prints the value 4.

It is possible to explicitly define the value of enumerator. These integer values can be positive or negative and can share the same value as other enumerators. Any non-defined enumerators are given a value one greater than the previous enumerator.

Note in this case, ANIMAL_HORSE and ANIMAL_GIRAFFE have been given the same value. When this happens, the enumerations become non-distinct -- essentially, ANIMAL_HORSE and ANIMAL_GIRAFFE are interchangeable. Although C++ allows it, assigning the same value to two enumerators in the same enumeration should generally be avoided.

Best practice: Don’t assign specific values to your enumerators.
Rule: Don’t assign the same value to two enumerators in the same enumeration unless there’s a very good reason.

Enum type evaluation and input/output

Because enumerated values evaluate to integers, they can be assigned to integer variables. This means they can also be output (as integers), since std::cout knows how to output integers.

This produces the result:

5

The compiler will not implicitly convert an integer to an enumerated value. The following will produce a compiler error:

However, you can force it to do so via a static_cast:

The compiler also will not let you input an enum using std::cin:

One workaround is to read in an integer, and use a static_cast to force the compiler to put an integer value into an enumerated type:

Each enumerated type is considered a distinct type. Consequently, trying to assign enumerators from one enum type to another enum type will cause a compile error:

As with constant variables, enumerated types show up in the debugger, making them more useful than #defined values in this regard.

Printing enumerators

As you saw above, trying to print an enumerated value using std::cout results in the integer value of the enumerator being printed. So how can you print the enumerator itself as text? One way to do so is to write a function and use an if statement:

Once you’ve learned to use switch statements, you’ll probably want to use those instead of a bunch of if/else statements, as it’s a little more readable.

Enum allocation and forward declaration

Enum types are considered part of the integer family of types, and it’s up to the compiler to determine how much memory to allocate for an enum variable. The C++ standard says the enum size needs to be large enough to represent all of the enumerator values. Most often, it will make enum variables the same size as a standard int.

Because the compiler needs to know how much memory to allocate for an enumeration, you cannot forward declare enum types. However, there is an easy workaround. Because defining an enumeration does not allocate any memory, if an enumeration is needed in multiple files, it is fine to define the enumeration in a header, and #include that header wherever needed.

What are enumerators useful for?

Enumerated types are incredibly useful for code documentation and readability purposes when you need to represent a specific, predefined set of states.

For example, functions often return integers to the caller to represent error codes when something went wrong inside the function. Typically, small negative numbers are used to represent different possible error codes. For example:

However, using magic numbers like this isn’t very descriptive. An alternative method would be through use of an enumerated type:

This is much easier to read and understand than using magic number return values. Furthermore, the caller can test the function’s return value against the appropriate enumerator, which is easier to understand than testing the return result for a specific integer value.

Enumerated types are best used when defining a set of related identifiers. For example, let’s say you were writing a game where the player can carry one item, but that item can be several different types. You could do this:

Or alternatively, if you were writing a function to sort a bunch of values:

Many languages use Enumerations to define booleans. A boolean is essentially just an enumeration with 2 enumerators: false and true! However, in C++, true and false are defined as keywords instead of enumerators.

Quiz

1) Define an enumerated type to choose between the following monster races: orcs, goblins, trolls, ogres, and skeletons.

2) Define a variable of the enumerated type you defined in question 1 and assign it the troll enumerator.

3) True or false. Enumerators can be:
3a) given an integer value
3b) not assigned a value
3c) given a floating point value
3d) negative
3e) non-unique
3f) initialized with the value of prior enumerators (e.g. COLOR_MAGENTA = COLOR_RED)

Quiz answers

1) Show Solution

2) Show Solution

3) Show Solution

4.5a -- Enum classes
Index
4.4b -- An introduction to std::string

194 comments to 4.5 — Enumerated types

  • Liam

    Hello,

    If we assign values inside enum are they evaluated during the compile time or by demand?

    For example, if instead of this:

    We will do this:

    Will the values that enum contains going to be evaluated when each variable of type Color will be defined (which could be at any time, including runtime) and thus it would be better to use literal 0x1 instead of expression 1 << 0 in terms of performance to avoid extra evaluations or the compiler pre-evaluates expressions within enum and accordingly it doesn't have any effect on performance?

    Thank you!

  • Aditi

    Where can i improve my code

    • Hi Aditi!

      * Line 13, 16, 19, 22, 25, 27: You don't need to explicitly call @std::string::string. You can just return the string literal and @std::string::string will be called implicitly.
      * Line 30: Initialize your variables with uniform initialization.

  • ayush

    help me only one error

    • nascardriver

      Hi ayush!

      Line 31: You need to pass a value to @print, eg. monstertype::monstertype_orgres.

    • nxbit01

      Hello ayush.

      I updated three things.
      1. I've setup a input value to be passed to the print function.
      2. changed print function to a void type.
      3. removed assigned value to enum types

  • danielnt

    Hey Alex you talked alot in the previous lessions on how to organize the code with separate files for function declarations and constants etc. But how would we do it with enums and structs? Cause I assume you want to organize them better if they start to add up. It would be great if you could cover this or refer to a later lesson.

    • Alex

      Basically, enums and structs are no different than functions in terms of organization. You want to put them in the smallest scope in which they are used. Typically, if they are used in a single .cpp file, they should go in that file. If they are used more broadly, then typically in a header file where they can be included into multiple files (with the headers themselves organizing related functionality).

  • Clownfish

    Let's say I define the following enum:

    and then want to assign the value of THING_BOX to a variable of type Thing. Should I be explicit when refering to an enum like this:

    or should I just do it like this:

    ? I come from C# where using the type as a prefix so to speak is necessary so I'm a little confused about this.

    • nascardriver

      Hi Clownfish!

      Be explicit. The next lesson covers enum classes, which require you to be explicit and should be preferred over regular enums.

  • Hervé

    Hi Alex

    I tried recreating your code under 'Printing enumerators' but with a switch statement instead. However it failed to compile and the compiler complained that the switch statement could not support enumerations. Did I do something wrong?

    EDIT: I solved it, turns out I was just misinterpreting what the compiler was telling me, I think the problem was I put single quotation marks around the enumerator.

    • nascardriver

      Hi Hervé!

      Since you didn't post any code and someone else might stumble across a similar problem in the future, here's how do write @getItemName using a switch-statement:

  • Rohit

    Where can I go to get more examples and ideas of how I can practice what I'm learning here? I understand everything so far, but my fear is that without actually using the things I'm learning in any kind of useful program, I'll end up forgetting certain important things.

    I'm still new to c++ so idk if I alone will be able to come up with useful real life scenarios of when I would want to use everything I'm learning.

    • Alex

      Try typing "C++ quiz" into a search engine and see if that's close enough to what you're looking for...

      But the best thing really is to give yourself challenges. One way to do that is extend the quiz programs in more complicated ways.

      • Rohit

        That's not exactly what I'm looking for, but still, that brings up good resources.

        I was looking more for real-world problems developers face that I would be able to solve with code. But I guess that's not something I can really do until I get further along in the tutorial.

        I like your suggestion of extending the quiz programs though. I've been doing that and it definitely helps.

  • Samira Ferdi

    I try to learn about enum types here but I just don't understand the use of enum explicitly.

    • nascardriver

      Hi Samira!

      Let's look at some examples

      This is problematic.
      * There are magic numbers.
      * If we wanted to change the value of an item we'd need to change it everywhere where it's used.
      * There's no way of knowing valid values for an item.

      To fix these problems by using constants for the item values

      This is better, but
      * It involves a lot of typing or copying to declare the constants.
      * We might accidentally use the same value for two different items.
      * There could be value gaps between two items if we were to delete an item type.

      That's were enums come in

      The enumerators are automatically numbers, so we don't need to worry about duplicates or gaps.
      We still need to type a lot if we want fully qualified names. Enum classes (Lesson 4.5a) will allow us to use shorter enumerator names.

      • Samira Ferdi

        Thank you for reply, nascardriver!
        My conclusion are those using enum give me clearer context (by not using magic number), my program become more modular (do minimalistic change), and the item of enum guarantee be uniqe.

        • nascardriver

          > item of enum guarantee be uniqe
          Only if you don't manually assign the same value twice

  • I tried recreating this my own way:

    could you please point out my mistakes?
    i know it has sth to do with the main calls.

    • nascardriver

      Hi Ali!

      You compiler showed you a lot of the issues, you should read them.

      You declared three variables, yet you never used them.

      You declared @getItemName to take one int as parameter. Now you're trying to call it without passing an argument.

      We had this before, that's not how to call a function. Lesson 1.4a

      You declared a variable but never use it.

      The parameter to @getItemName has the same name as a function, this should be avoided.
      You don't need to explicitly call the std::string constructor, your compiler is smart enough to do it for you.
      Inconsistent use of initializers.
      Copy-pasting won't get you anywhere, you should try to understand and write this code on you own. If you feel unconfident you should go back to previous quizzes and try to solve them without copying.

      • Hi!

        i've done it this way, is it good now?

        OH and isnt this easier?

        • nascardriver

          @itemType and everything within is still unused.
          You're still using two different kinds of initializers.

          > isnt this easier?
          No, using an enum is easier, it offers named numbers that are automatically assigned values to prevent duplicates. You solution doesn't.

          To keep you going, try this:
          - The user enters a number (0, 1 or 2)
          - Your program prints the corresponding item type
          Do this by only modifying your main function. I'm asking you to do this, because you'll need to understand function parameters and arguments for all lessons.

  • Hi nascardriver,

    will i ever have to use this in my life?
    because i dont really understand this

  • Eri

    Hi Alex,

    I dont understand this part:

    // ItemType is the enumerated type we've defined above.
        // itemType (lower case i) is the name of the variable we're defining (of type ItemType).
        // ITEMTYPE_TORCH is the enumerated value we're initializing variable itemType with.
        ItemType itemType(ITEMTYPE_TORCH);

    itemtype is the name of the variable, can you further explain this? and why we right it this way ItemType itemType(ITEMTYPE_TORCH); ?

    • nascardriver

      Hi Eri!

      > itemtype is the name of the variable, can you further explain this?
      itemType is a variable like every other, it could have any other name too.

      > why we right it this way ItemType itemType(ITEMTYPE_TORCH); ?
      I don't know why Alex used a direct initialization here, he seems to use copy initializations almost everywhere else.
      These would also work

      Lesson 2.1 covers the different types of initializations available in C++.

  • Ned

    What does color mean and do? Not Color.

  • Weckersduffer

    Just when it finish compiling, it appears an error message that says: "enum.exe finished working"

    • nascardriver

      Hi Weckersduffer!

      I guess you mean the Windows error message "enum.exe has stopped working".

      Your function @get doesn't return a value, this causes undefined behavior and is most likely responsible for your crash.

      @main also doesn't return a value, I suggest you enabling all warnings in your compiler/IDE.

    • Maxixxx

      Two things are wrong:
      1. Replace each "std::cout <<" in get() with "return".
      2. Add return 0; to the end of main().
      I think your program will work then.

  • RAR

    Hi Alex. A small (nitpicking) doubt in the quiz. The first question asks us to "define" an enum. Shouldn't it be "Declare" an enum? And the second question should be "DEFINE a variable of the enumerated type you DECLARED in question 1 and assign it the troll enumerator." right? I'm just trying to apply what I've learnt so far like a practice!

    • Alex

      Quiz question 1 was actually written correctly. However, the lesson incorrectly used the word "declare" instead of "define" when talking about defining an enum, which is why you are confused. I've updated the lesson as appropriate.

      Quiz question 2 was wrong as you identified. I've updated the quiz question to ask you to define a variable of the enumerated type.

      Thanks for pointing these out!

  • Hamed O.Khaled

    Hi Alex!
    I'm trying to develop a simple chess engine right now , But I confused about the necessity of using enumeration data types to represent pieces of chess can't we replace it with string piece [] = "WHITE_PAWN", ... it will suffice the symbolic goal too ..

    • nascardriver

      Hi Hamed!
      Strings use more memory, are slower, and you could accidentally modify them at runtime. Also, when you want to check if a piece is eg. a bishop you might misspell a string causing your program to fail.

      Enums are the way to go.

    • Alex

      In addition to what nascardriver said, enums are just integers, so comparison is fast. String comparison is comparably slow.

      If you later decided to change the name of an enumeration, the compiler will complain if it finds an enumerator it doesn't recognize. If you use strings, you'll have to manually update all of the strings yourself and be careful not to make any mistakes.

      Enumerators can also be used as indices for arrays, whereas strings can not.

      In short, use enumerators for your pieces, and then you can use these to index an array of names if you want to convert an enumerator to a string.

  • Alok

    For Printing Strings Better way could be..
    enum Color
    {
        COLOR_BLACK, // assigned 0
        COLOR_RED, // assigned 1
        COLOR_BLUE, // assigned 2
        COLOR_GREEN, // assigned 3
        COLOR_WHITE, // assigned 4
        COLOR_CYAN, // assigned 5
        COLOR_YELLOW, // assigned 6
        COLOR_MAGENTA, // assigned 7
        COLOR_MAX   ///assiged 8

    };

    strings sColor[COLOR_MAX]
    {
        "COLOR_BLACK", // assigned 0
        "COLOR_RED", // assigned 1
        "COLOR_BLUE", // assigned 2
        "COLOR_GREEN", // assigned 3
        "COLOR_WHITE", // assigned 4
        "COLOR_CYAN", // assigned 5
        "COLOR_YELLOW", // assigned 6
        "COLOR_MAGENTA" // assigned 7
    };

    void printColor(Color color)
    {
           if(color < COLOR_MAX)
             std::cout << sColor[color];
           else
            std::cout << "Who knows!";
    }

  • Hema

    In line 5 and 12, why didn't the enumerators end with a semi-colon?

    • nascardriver

      Hi Hema!
      The last entry of an enum doesn't need a comma, it's optional.
      Using a comma after the last element makes it easier to add more entries, there's no semantic difference though.

  • Hello Alex and Happy New Year!

    According to the C++ Core Guidelines, it seems the use of ALL_CAPS (á la macros} in enums and enum classes is frowned upon. From my own perspective, I find it more useful, handy and readable. What's your take?

    • Alex

      Personally, I like using all caps for enumerators (and macros), but that's likely because it's what I'm used to. Google suggests that naming enumerators like constants is better since they are essentially constants, and this helps avoid naming collisions with macros.

      I think they have a valid point, but I probably won't change. 🙂

  • Benjamin

    Hi Alex,

    thanks for the great tutorials!

    I am playing around with sorting algorithms inspired by the later lesson on selection sort. I am aiming at writing a header file including a general "sortArray"-function, to which the user can pass an argument for picking the algorithm he wishes to apply. For this argument i defined an enum class called "Algorithm". Now the forward declaration of my "sortArray"-function inside the header file needs that enum type as well as the header's cpp-file hosting the actual function.

    Is it good practice to make a header file just for the enum definition and include it to the "main-header" as well as its underlying cpp? It feels a bit wrong to me to include a header into a header. Is there any better way?

    • Alex

      It's fine to include a header in a header if the including header needs the definitions in the included header.

      However, in this case, if your Algorithm enum is only used in the header containing the function forward definitions that use the Algorithm type, then I'd probably just define Algorithm in that same header (you can always split it out later if needed).

      • Benjamin Kambs

        Thanks for your answer and Merry Christmas first of all (even though it is basically over here by now).

        I did not idle myself and kept digging a bit more about the topic. I read you actually stated that it is a common thing to include headers into headers (I guess in the lesson about header guards). Sorry I did not cross check enough. However, another issue in that context puzzled me a bit. I found the answer to this one as well, but I like to hear from a programming veteran, whether my interpretation on why c++ was constructed like that is correct:

        The way I designed my program, the enum definition was included into my main.cpp (via the header in the header) and also into my second cpp (via the enum definition header directly). I did not pay attention first, but later remembered that having a function defined in multiple files would cause a linker error. So why not with enum definitions? Reading the wiki article about the one-definition-rule I saw that type definitions are only needed to be unique within a single translation unit. Just one possibility came to my mind, which would justify this different treatment: in some cases the compiler might need to know details about types. For example if a cpp file uses certain enumerations, it is clearly not enough to just declare the corresponding enum without defining it.  On the contrary the compiler does not care about what a function is doing as long as the functions's parameters and return types fit - which is guaranteed by the forward declaration. Bottom line: in contrast to functions, multiple definitions can't be entirely avoided for types. Therefore, the one-definition-rule is less strict on types. Is that about right?

        • Alex

          Yes, that's right. With variables and functions, the compiler only needs to know about the variable or function prototype to do syntax checking. However, with types, the full type definition needs to be known. This necessitates including the full type definition into the places where it is used. Therefore, the one definition rule is relaxed for types.

  • fhmadcode

    Something about the Quiz 3f
    I have tested it in MSVC 2017, however, it is not a legal assignment.
    And the complier gave an error that "lvalue should be a modifiable value".
    So I think 3f is false?
    Do I make any mistake or misunderstand about this?
    My code is here:

    • Alex

      The wording on the question was poor (and has been updated). When the enumeration is being defined, you can initialize the enumerator with any valid integer value (or value that can be converted to an integer, which includes other enumerators). However, once defined, those enumerator values can't be remapped, which is why your statement doesn't work.

  • ZhiLier

    Hi Alex, I don't know why "enumerated type declaration doesn’t take any memory", can you explain something about it ?

    • Alex

      Sure. When you're defining a user-defined type, you're just giving your type a name, and telling the compiler what it looks like. If you never use the type, no memory is needed. It's only when you declare a variable of that type that memory is allocated.

      As an analogy, if I say, "An Apple is a fruit and can come in red, green, and yellow", that tells you a bit about an apple (I've defined what an apple is). But how many apples do I have? None at this point, Apple is just a definition. I don't need to reserve space in my fruit bowl until I acquire some actual apples.

      • ZhiLier

        I see, Thank you!

      • Rohit

        So I guess this means that the enumeration definition simply takes space in the source files, but not in the exe?

        Speaking of exe files, I'm not getting any when I build my code in VS 2017. Is that simply because it's in debug mode or is there something else?

        • nascardriver

          > So I guess this means that the enumeration definition simply takes space in the source files, but not in the exe?
          Correct.

          > I'm not getting any when I build my code in VS 2017
          You should get a message in the build log (bottom of VS) telling you where the executable is located.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]