Search

S.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 or enum) 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.

Naming enumerations and enumerators

Providing a name for an enumeration is optional, but common. Enums without a name are sometimes called anonymous enums. Enumeration names are often named starting with a capital letter.

Enumerators must be given names, and typically use the same name style as constant variables. Sometimes enumerators are named in ALL_CAPS, but doing so is discouraged, because it risks collisions with preprocessor macro names.

Enumerator scope

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:

If you want to use a different integer type for enumerators, for example to save bandwidth when networking an enumerator, you can specify it at the enum declaration.

Since enumerators aren’t usually used for arithmetic or comparisons, it’s safe to use an unsigned integer. We also need to specify the enum base when we want to forward declare an enum.

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 can only forward declare them when you also specify a fixed base. 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, old functions sometimes 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 time

Question #1


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

Show Solution

Question #2


Define a variable of the enumerated type you defined in question 1 and initialize it with the troll enumerator.

Show Solution

Question #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)

Show Solution


S.4.5a -- Enum classes
Index
S.4.4c -- Using a language reference

301 comments to S.4.5 — Enumerated types

  • Rishi

    This following code is intended to print 12 but it's printing 11.I can't figure out the exact issue here. I'll be happy if I'm helped

  • Rishi

    I have some questions and I'll be grateful if you answer those

    1.What is the use of forward declaring enums?
    2.Can enumerators hold string values?
    3.What types of values can enumerators hold?

  • Rishi

    Why can't be an enum value assigned with an integer value?

    This works but

    This causes error. I don't get it. How come we can assign integer values in defining place but not outside?

  • Rostyslav

    Could someone please explain to me how this `static_cast`ing to enum is working? Consider the following program:

    ```
        #include <iostream>

        enum Color
        {
            color_black, // assigned 0
            color_red // assigned 1
        };

        int main()
        {
            std::cout << "Enter a number: ";
            int inputColor;
            std::cin >> inputColor;

            auto color{ static_cast<Color>(inputColor) };

            std::cout << "You chose color " << color << '\n';

            std::cout << color_black << ", " << color_red << '\n';

            return 0;
        }
    ```

    If I input 7 to the program, I will get as an output: "You chose color 7", followed by "0, 1". So effectively somehow the variable `color` holds value 7, while it should be able to hold either of the two symbolic constants: 0 and 1. What am I missing?

    I also did not get that note at the beginning of the article about comma after the last enumerator. What does it do exactly?

    • nascardriver

      It's hard to understand something that isn't there. I removed the note but left the comment as it was >,<. Lesson updated, what it was referring to is

      A variable of enum type can hold all the values that the underlying type of your `enum` can hold. The underlying type of `Color` is likely `int` (Can be a different type), so your `color` variable can hold all values that an `int` can hold. Obviously you don't have enumerators for all those values, that's why you should validate the user's input before casting it to an `enum` type.

  • TonyCheeze

    Hello,
    First I wanted to say thanks for this awesome tutorial so far. Learning C++ has been amazing! That being said, I must say that this module on Enumerated types has been a real headache for me. I don't fully understand why this doesn't work:

    I know you've answered some other peoples questions on the topic of forward declaring an enum and they seemed satisfied with it, but to be honest your answers just kind of confused me more than anything. For example, in one of your responses to user sami you said, "You can't use enumerators before the enum is defined. The forward declaration only allows you to declare enum variables (eg. as parameters in a forward declaration of a function)." Could you please clarify this with a concrete example? I eventually altered my code like this because of the way I interpreted your answer:

    But I get the same old error. Any help would be greatly appreciated, again with a real concrete example. Thank you!

    • nascardriver

      You cannot use the enumerators until the enum is defined. You can only use the enum type (`Item`) of a forward declared enum.

      • TonyCheeze

        Thank you for the quick response! But what purpose does forward declaring an enum serve if you're just going to put the enum definition above main() anyways? So far, the only real reason to forward declare has been to tell the tell the compiler of the existence of a variable/function before running main() (i.e. we've forward declared functions because they were defined after main() or we've forward declared external variable because they were in other files) but I don't really see a point here. If Item is already above main(), why the forward declaration? In other words, why doesn't this work:

        • nascardriver

          Circular dependencies can prevent you from being able to define the enum/including the file in which the enum is defined:

          a.hpp

          b.hpp

          c.hpp

          main.cpp

          (This is incomplete, imagine this file structure was required)

          • TonyCheeze

            I'm sorry, but I guess I just don't fully grasp what an enum is. I think that's where the disconnect is happening. I view enums as just sort of a "container" for a bunch of related symbolic constants. But is that not it? So when I forward declare in this way

            is this not telling the compiler of the existence of the contents inside Item also? Sorry, normally I'm pretty quick to grasp concepts but this one doesn't make a lot of sense to me and I think I'm overthinking it.

            • nascardriver

              only tells the compiler that there's an `enum` with name "Item" and its underlying type is `int`. It doesn't tell the compiler about the enumerators (Names of the constants) inside the `enum` or what values the enumerators have.

  • oliva

    why do you use the name of enum instead of directly assign the value of enum?

    example:
    int main(){

    enum color{
       color_black,
       color_pink
    };
    color paint = color_black; //this style?
    //vs
    int paint = color_black;   //this style?

    return 0;
    }

    //thank you.

  • Math

    why doesn't this code work? I already put a forward declaration for enum Color
    I get an error message saying that COLOR_BLUE is an undeclared identifier

    • nascardriver

      You cannot use enumerators before the enum has been defined. You can only use a forward-declared enum to forward-declare functions.

  • c

    Hi I have 2 questions for Question #2

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

    1) solution is:

    but shouldn't it be

    2) Why must we put a scope operator?

    • nascardriver

      Both work, you don't need to use the fully scoped name. I used the fully scoped name, because that's what you usually do, because usually you use enum class (Next lesson)

      • Spero

        It's my humble opinion that you should either use

        or explain that the above answer is fine but the provided answer is more correct and will be explained in the next lesson, so that people like me and 'c' don't have to scour the lesson again, find nothing, then come to the comments section for an explanation. Thanks for all your hard work!

  • sami

    Hi,
    I have some questions:

    1) TYPO: 'e' is missing from 'numerator':

    'If there is no previous enumerator, the numerator will assume value 0.'

    2) Would you please give an example? I got compile error by doing that!
    "Since enumerators evaluate to integers, and integers can be assigned to enumerators, enumerators can be initialized with other enumerators (though there is typically little reason to do so!"

    3) Got error on enum forward declarations! I got "COLOR_WHITE: undeclared identifier" in the following code. I don't know why?

    • nascardriver

      1) Thanks

      2)

      3)
      You can't use enumerators before the enum is defined. The forward declaration only allows you to declare enum variables (eg. as parameters in a forward declaration of a function).

      • sami

        Thank you so much.

        • Tony

          Excuse my ignorance, this returns 0 1 2 3. Shouldn't it return 0, -1, -2 and -3 or it doesnt matter at all?

  • Nahin

    >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.

    Would you please elaborate more on it. I didn't get why we should avoid having two enumerators with the same value?

  • AE35_Unit

    Answers to #1, 2, 3 and my questions on enums. Questions are commented in the code. Please let me know if anyone needs clarification.  Thanks all.

    • nascardriver

      You will mostly prefix enumerators with their enum's name (After the next lesson). If you do that, `auto` can be used without compromising information.

      Before you cast something to an enum, you should verify that the value has a corresponding enumerator.

      > are LOCHNESS and NESSIE part of the "enum Monster" now?
      No, they're variables with the type `Monster`. The should follow the same naming convention as all your variables do.

      • AE35_Unit

        Thanks for the clarification. Catching up and finding where I left off and doing a bit of review. I look at two other languages, C and Java, plus I got a raspberry pi and I'm having terrible amounts of fun with it.  Thanks for all your help.
        AE35_Unit

        ~HAL did nothing wrong.

  • Innervate

    For my answer to Q2 of the quiz I had the following:

    I based my answer off how in the last section ItemType Torch is defined:

    The solution shows that this was not the preferred method. I then changed my code to the following:

    Is this correct? shouldn't the example of ItemType Torch also be changed to reflect the preferred method eg.

    • nascardriver

      I updated the lesson to use list initialization, thanks for pointing out the inconsistency!
      The enum name prefix isn't necessary for `enum` types, use it as you wish. The next lesson introduces `enum class`, where the prefix is mandatory.

  • Van

    Output

    Enter: 9
    You entered 9

    Alex wrote "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"

    Does it mean 9 is assigned to an enumerator?  If yes, what is that enumerator?

    • nascardriver

      Variables of `enum` type can hold any value that can be stored by their underlying type (Usually `int`). This makes the cast legal, even if the input is 9, but there is no enumerator for 9. You can compare every of your enumerators to `color`, but none will match.
      If you want to make sure that the user entered a valid value, you can add another enumerator to the end of `Color`, eg. `COLOR_MAX`, and check if `inputColor >=0 && inputColor < Color::COLOR_MAX` before doing the cast. This only works if your enumerators are contiguous, ie. there are no gaps between enumerators because you manually assigned values.

  • Eric

    Hi,
    This probably slipped by me earlier but in your solution to quiz 2:

    Why initialise monsterType that way instead of { MONSTER_TROLL };  // which worked in the codeblocks compiler?
    Thanks!

    • nascardriver

      If there's another `enum` with a `MONSTER_TROLL` enumerator, there will be a name conflict. This isn't applied consistently in this lesson. The next lesson will show a good alternative to `enum` that enforces the prefix.

  • cgsousa

    Is there a possibility to extract automatic the names from the values using RTTI (Runtime Type-Information)?

  • Raffaello

    is there a difference between the three?

    also, is this a valid exception to the "Don’t assign specific values to your enumerators" best practice?

    • nascardriver

      > is there a difference between the three?
      See my comment here.

      > is this a valid exception
      It depends on how the enumerators are used. If they're printed out, it can be useful to have specific values so that you can easily trace back the number to an enumerator. In this case, `ParseResult` is only used internally by our code, so there's no need for specific values. I updated the lesson, thanks for pointing it out!

  • bobby smith

    is there a reason you didn't use brace initialization in the answer to question 2?

  • Thomas Kennings

    "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."

    Can we get these in green boxes? I always feel good when I see the green boxes coming up because they tidy up my questions.

    • nascardriver

      Lesson updated. I only update the style when it gets pointed out or I edit the lesson for other reasons. If you see any more lessons, feel free to point them out and I'll get you nice green boxes :-)

  • antiriad7

    Hi,
    I wonder, can I define an enum, struct or another function inside a function? I think I missed something about possible places of definitions in this tutorial.

  • SappyB

    Is it considered "Best Practice" to use switch statements with enumerations? Or is if-else preferred? I'm guessing that switch statements would be "Best Practice", because enumerations are integers-in-order kinda like cases in switch statements. Also, would using switch statements in this case be more performant than if-else?

    • nascardriver

      Switches are preferred for limited sets of values (chars, enums). They're easier to read (Because you know there can't be comparisons other than equality) and the compiler can optimize them better.

  • AbraxasKnister

    There's an occurrence of non colorized best practice and rule.

    • nascardriver

      Thanks for pointing it out! The lessons are gradually updated to the new style, but it takes some time.

      • AbraxasKnister

        I'm working myself through the this lecture series slowly but constantly and might make similar remarks in the future (I already encountered the same in the lectures about pointers and arrays). I figure you might find it helpful so just leave it uncommented and ignore it if you already know.

  • Armitage

    In forward declaration yes, but do we need specify base in definition? I have no error if I don't.

    • nascardriver

      Yes, every redeclaration has to have the same base specifier as the previous declarations. If your compiler doesn't error out, you might have forgotten to disable compiler extensions (Lesson 0.10).

      • Armitage

        You are right. But I have another problem:

        'COLOR_BLACK': undeclared identifier. So How i must use enum forward declaration correctly?

        • nascardriver

          You can't use an enumerator before it's defined. A forward declaration of an enum can be used to forward declare a function.

          In a small single-file project, this doesn't make sense. You might need it when you separate your code into multiple files to prevent circular dependencies.

          • Armitage

            I think I get It. Thanks.
            But VS2019 let you omit base type on enum declaration if it int.

            • nascardriver

              Yep, I double checked and VS doesn't error out even with /Za and /permissive-. You'll have to pay attention yourself not the omit the base when you redeclare the enum. This is non-standard behavior, if you rely on this, your code won't compile with other compilers.

  • kavin

    Hi, i have written a program based on 1st example. My doubt is, if its ok like the user below "Ri" has written the program without using MonsterClass enumeration parameter in the getRace() by directly accepting values as int? Without using a static_cast how will his program convert int input to compare with enumerators in MonsterRaces ?
    Here's my code. Is this ok or any improvements needed ?

    • nascardriver

      enumerators of an `enum` can be compared to an `int` without a cast, though, casting the `int` first is better to avoid mixed types.

      - Line 15-19: Those are magic numbers/strings. If you update `MonsterRaces`, this list is incorrect. Use your enumerators to print the selection.

      • kavin

        If i use without a cast i get this error:
        (42,40): error C2440: 'initializing': cannot convert from 'initializer list' to 'MonsterRaces'
        (42,40): message : Reason: cannot convert from 'int' to 'MonsterRaces'

        So i guess static_cast is compulsory in my case. I am using VS2019.

        For the line 15-19 i am not sure how to do that :( Even if i do a function with something like,
        if(MONRACE_ORCS==0)
        std::cout<<"Orcs-0\n";
        if(MONRACE_GLOBLINS==1)
        std::cout<<"Globlins-1"
        etc.,
        it would not work properly if i change the order or add a new race in enum MonsterRaces right? What is the code to do that?

        • nascardriver

          > If i use without a cast i get this error
          You can't initialize an `enum` with an `int`, you can only compare it to an `int`.

          Your solution would skip the monster if the enumerator's value is not the same as the value in the string. You still have a magic number/string. You can print the enumerators, rather than writing a value yourself.

      • kavin

        Sorry for double reply. I changed 15-19 like this but it wont print "Orcs-0". But it would out put as orcs if i input 0. I dunno why it is not getting printed ! Is this the correct way or ?

  • Charan

    Hey,It is stated that the enumerator can be input through keyboard. Let's say the enumerators are assigned(by us or by the system) like COLOR_BLUE = 3,COLOR_RED = 5. What if I entered a value of 7 in some integer variable x, and then static cast x into the type Color?

    • nascardriver

      All values that are valid for an enum's underlying type (eg. int), are valid for the enum. If you let the user input a value, or create an enumerator via a cast in general, you should verify that the value is one of your enumerators.
      If you manually assigned values to your enumerators, you'll have to check one after the other. If they were numbered automatically, you can check if the untrusted value is a value is in the range from your lowest enumerator to your highest enumerator.

  • ErwanDL

    Hey Alex, why is it still written "Because the compiler needs to know how much memory to allocate for an enumeration, you cannot forward declare enum types.", although you wrote a few lines above that forward declaration of enums is possible (since C++11 if I'm right), as long as you also forward declare the enum base ?

    • nascardriver

      Hi!

      I missed that sentence when I added the paragraph about forward declarations. Base specifications and forward declarations are indeed possible since C++11. Thanks for pointing it out! Lesson updated.

  • Ri

    Love the tutorials so far! Good job making them very intuitive. I tried to create a sort of spawning system for game (with what little knowledge I have!), any tips or ways to improve this code would be greatly appreciated. Many thanks.

    • nascardriver

      Hi!

      - If your program prints anything, the last thing it prints should be a line feed ('\n').
      - Line 6 uses neither `monster` nor `monName`. If you update the string, `monName` or `monsters`, your program prints wrong instructions.
      - Line 26, 28, etc: You don't need to manually call `std::string`.
      - Avoid abbreviations.
      - `monName::monToSpawn` should be a `monster`.

    • Elis

      Hi! I fiddled a bit with this code to accept text input as opposed to integers.

      If nascardriver or anyone is around I'd be interested in hearing their opinion on the usage of the '?:' operator,
      too long and convoluted?

      • nascardriver

        > opinion on the usage of the '?:' operator
        The problem is that it's not reusable. Add `getMonsterByName` and `getNameOfMonster` with the conditionals inside, then it's ok.
        "ok", because in reality you would use different techniques that you don't know about yet. Your code shows that you understood the conditional operator, that will come in handy in other quizzes.

        > 32767
        Magic number. There's nothing stopping this value from being valid. Add a `MONSTER_INVALID` to `monsters` and return that instead. It's obvious in the code that this is an invalid value and it won't be a duplicate.

  • Omri

    "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"
    Is it not a postfix in this example...

  • Mhz93

    In the last line of the first example of this lesson, the word "red" has been printed in black color which should be blue!!
    Also the same with the word "GREEN" in the second example.
    Also the word "POTION" in line 8 in the 15th example.
    Also the word "BACKWARDS" in line 4 in the 16th (the last) example.
    and finally "SKELETON" in 7th line in the solution of first queez.
    sorry I know that's not worth to mention!! but as I like this site and enjoy learning CPP from, I like to see this as well as possible.

  • Kris

    Hi nascardriver,

    No wonder there is so many comments on this topic. I found it very confusing myself, too.

    I have one comment: declaring ItemType itemType =  ITEMTYPE_TORCH is very confusing; why not to declare it this way: ItemType weapon = ITEMTYPE_TORCH. Just too many "itemtypes" for a novice. Couldn't make use of the function getItemName() to print out the name of the weapon.

    Although it forced me to read this part of the lesson many times, play with it in Visual Studio many times until I got it! :)

    Thanks.

Leave a Comment

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