Enumeration

Basic Enumeration Declaration

Standard enumerations allow users to declare a useful name for a set of integers. The names are collectively referred to as enumerators. An enumeration and its associated enumerators are defined as follows:

enum myEnum
{
    enumName1,
    enumName2,
};

An enumeration is a type, one which is distinct from all other types. In this case, the name of this type is myEnum. Objects of this type are expected to assume the value of an enumerator within the enumeration.

The enumerators declared within the enumeration are constant values of the type of the enumeration. Though the enumerators are declared within the type, the scope operator :: is not needed to access the name. So the name of the first enumerator is enumName1.

C++11

The scope operator can be optionally used to access an enumerator within an enumeration. So enumName1 can also be spelled myEnum::enumName1.

Enumerators are assigned integer values starting from 0 and increasing by 1 for each enumerator in an enumeration. So in the above case, enumName1 has the value 0, while enumName2 has the value 1.

Enumerators can also be assigned a specific value by the user; this value must be an integral constant expression. Enumerators who's values are not explicitly provided will have their value set to the value of the previous enumerator + 1.

enum myEnum
{
    enumName1 = 1, // value will be 1
    enumName2 = 2, // value will be 2
    enumName3,     // value will be 3, previous value + 1
    enumName4 = 7, // value will be 7
    enumName5,     // value will be 8
    enumName6 = 5, // value will be 5, legal to go backwards
    enumName7 = 3, // value will be 3, legal to reuse numbers
    enumName8 = enumName4 + 2, // value will be 9, legal to take prior enums and adjust them
};

Enum forward declaration in C++11

Scoped enumerations:

...
enum class Status; // Forward declaration 
Status doWork(); // Use the forward declaration
...
enum class Status { Invalid, Success, Fail };
Status doWork() // Full declaration required for implementation
{
    return Status::Success;
}    

Unscoped enumerations:

...
enum Status: int; // Forward declaration, explicit type required
Status doWork(); // Use the forward declaration
...
enum Status: int{ Invalid=0, Success, Fail }; // Must match forward declare type
static_assert( Success == 1 );

An in-depth multi-file example can be found here: Blind fruit merchant example

Enumeration in switch statements

A common use for enumerators is for switch statements and so they commonly appear in state machines. In fact a useful feature of switch statements with enumerations is that if no default statement is included for the switch, and not all values of the enum have been utilized, the compiler will issue a warning.

enum State {
    start,
    middle,
    end
};

...

switch(myState) {
    case start:
       ...
    case middle:
       ...
} // warning: enumeration value 'end' not handled in switch [-Wswitch]

Iteration over an enum

There is no built-in to iterate over enumeration.

But there are several ways

  • for enum with only consecutive values:

    enum E {
        Begin,
        E1 = Begin,
        E2,
        // ..
        En,
        End
    };
    
    for (E e = E::Begin; e != E::End; ++e) {
        // Do job with e
    }
    
C++11

with enum class, operator ++ has to be implemented:

E& operator ++ (E& e)
{
    if (e == E::End) {
        throw std::out_of_range("for E& operator ++ (E&)");
    }
    e = E(static_cast<std::underlying_type<E>::type>(e) + 1);
    return e;
}
  • using a container as std::vector

    enum E {
        E1 = 4,
        E2 = 8,
        // ..
        En
    };
    
    std::vector<E> build_all_E()
    {
        const E all[] = {E1, E2, /*..*/ En};
        
        return std::vector<E>(all, all + sizeof(all) / sizeof(E));
    }
    
    std::vector<E> all_E = build_all_E();
    

    and then

    for (std::vector<E>::const_iterator it = all_E.begin(); it != all_E.end(); ++it) {
        E e = *it;
        // Do job with e;
    }
    
C++11
  • or std::initializer_list and a simpler syntax:

    enum E {
        E1 = 4,
        E2 = 8,
        // ..
        En
    };
    
    constexpr std::initializer_list<E> all_E = {E1, E2, /*..*/ En};
    

    and then

    for (auto e : all_E) {
        // Do job with e
    }
    

Scoped enums

C++11 introduces what are known as scoped enums. These are enumerations whose members must be qualified with enumname::membername. Scoped enums are declared using the enum class syntax. For example, to store the colors in a rainbow:

enum class rainbow {
    RED,
    ORANGE,
    YELLOW,
    GREEN,
    BLUE,
    INDIGO,
    VIOLET
};

To access a specific color:

rainbow r = rainbow::INDIGO;

enum classes cannot be implicitly converted to ints without a cast. So int x = rainbow::RED is invalid.

Scoped enums also allow you to specify the underlying type, which is the type used to represent a member. By default it is int. In a Tic-Tac-Toe game, you may store the piece as

enum class piece : char {
    EMPTY = '\0',
    X = 'X',
    O = 'O',
};

As you may notice, enums can have a trailing comma after the last member.



2016-07-22
2016-08-23
C++ Pedia
Icon