Search

8.2 — 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


8.3 -- Enum classes
Index
8.1 -- Using a language reference

322 comments to 8.2 — Enumerated types

  • Navigator

    My solution runs but I get the warning "The enum type 'Monster_type' is unscoped. Prefer 'enum class' over 'enum'".

    • nascardriver

      Line 13-17: Those are magic numbers, use your enumerators.
      You don't need to `break` after a `return`, a `return` stop everything.
      We cover `enum class`, a good alternative to `enum`, in the next lesson. This is what your compiler wants you to use.

  • J34NP3T3R

    Defining an enumeration (or any user-defined data type) does not allocate any memory

    QUESTION : How does the program remembers this enum and its enumerators without using memory ?

    EDIT : oh it says here that it may actually use memory.

    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.

    so what does "Defining an enumeration (or any user-defined data type) does not allocate any memory" mean ?

  • kio

    Hi Alex and nascardiver,
    Under the "Printing enumerators" section, you can now switch if/else condition for switch (because the switch is introduced in the Chapter 7.)

  • kio

    Solution for Q1 and Q2:

  • hi

    For the solution to Quiz #2, why use the "auto" type rather than "MonsterType" when defining the variable?

    In a previous example, we did:

    rather than

    • hi

      I'm still confused on this, so if anyone comes up with an answer, I'll be happy to hear it!

    • nascardriver

      The first time a new type gets introduced, I prefer avoiding `auto` to show exactly what type it is.
      That might be why the previous examples didn't use `auto`. It could also be that those examples simply haven't been updated yet.
      If the type is obvious, as in `ItemType::itemtype_torch`, use `auto` to avoid the redundant type in the declaration.

  • Q.1 & Q.2
    header.h

    main.cpp

  • sargiof

    Are enums secretly static since they can be used independent of an object? And are they compile-time constants like constexpr?

  • yeokaiwei

    Could you add a few more rules to your enum naming convention?

    1. Do not use "_" as a prefix
    E.g. _01, _02, _03
    It can conflict with internal flags

    2. Do not use ALLCAPS
    E.g. HEIGHT, AGE, SEX
    It can conflict with macros.

    3. Use strings as enum values
    E.g. Height, Age, Sex

  • sargiof

    Why can't I use non-static members inside an enum? SPRITE_SIZE_H is a const int member of
    the class and i wanted to use it for expressions inside the enum but I get errors stating "invalid use of a non-static data member".
    I know there are other ways to go about this but I want to understand why this doesn't work in particular.
    Here is a condensed version of the code:

Leave a Comment

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