C++ Enumeration Declarations

This topic pertains to the ISO Standard C++ Language enum type. For information about enums in C++/CLI and C++/CX, see enum class (C++ Component Extensions).

An enumeration is a user-defined type consisting of a set of named constants called enumerators.

enum [identifier] [class|struct] [: type] {enum-list} [declarator];   // for definition of enumerated type

enum identifier declarator;   // C-style declaration of variable of type identifier. See Remarks.

Parameters

  • identifier
    The type name given to the enumeration.

  • class or struct
    Optional scoped enum specifer. See Remarks.

  • type
    Optional underlying type of the enumeration. See Remarks.

  • enum-list
    List of the enumerators contained by the enumeration. The names of enumerators must be different from any other enumerator or variable in the same scope. However, the values can be duplicated.

  • declarator
    Declarator list that specifies the names of the enumeration. For more information, see Overview of Declarators.

Remarks

An enumeration provides context to describe a range of values. The following example shows an unscoped enumeration that contains the four suits in a deck of cards.

enum Suit { Diamonds, Hearts, Clubs, Spades };

Every name of the enumeration becomes an enumerator and is assigned a value that corresponds to its place in the order of the values in the enumeration. By default, the first value is assigned 0, the next one is assigned 1, and so on. You can set the value of an enumerator.

enum Suit { Diamonds = 1,
             Hearts, 
             Clubs,
             Spades };

The enumerator Diamonds is assigned the value 1. This affects the values that are assigned to subsequent enumerators; Hearts is assigned the value 2, Clubs is 3, and so on.

The name of every enumerator is treated as a constant and must be unique within the scope where the enum is defined, but the values do not have to be. Any enumerator in the list, including the first one, can be initialized to a value other than its default value. Suppose the declaration of Suit had been the following:

enum Suit {
   Diamonds = 5,
   Hearts,
   Clubs = 4,
   Spades
};

Then the values of Diamonds, Hearts, Clubs, and Spades would have been 5, 6, 4, and 5, respectively. Note that 5 is used more than once, which may not be intended.

Declaration rules for enum are slightly different in C and C++. In C, the enum keyword is required to declare an enumeration.

enum Suit { Diamonds, Hearts, Clubs, Spades };
 
void shuffle()
{
    enum Suit current;
    // ...
    current = Clubs;
    // ...
}

In C++, the enum keyword can be omitted. For example:

enum Suit { Diamonds, Hearts, Clubs, Spades };
 
void shuffle()
{
    Suit current;
    // ...
    current = Clubs;
    // ...
}

Implicit conversion can be confusing because unscoped enums can be converted to int, but not the other way. If you try to assign hand a value that is not a day of the week,

int account_num = 12345678;
Suit hand;

hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'

the compiler flags this assignment as an error at compile time. A cast is required to convert an int to an enumeration. However, you can go the other way and promote an enumerator to an integer value without a cast.

int account_num = Hearts;

But why would you want to do that? Using implicit conversions in this way can lead to unintended side-effects. In C++11, we now have scoped enumerators that are also strongly typed, and would generate an error for the assignment shown in the previous example. Let's look at some other advantages of scoped enums. With unscoped enums, you can have this situation:

enum Suit { Diamonds, Hearts, Clubs, Spades };
 
enum Tools { Picks, Shovels, Spades, Hammers }; // error C2365: 'Spades' : 
                                                // redefinition; previous definition was 'enumerator'

Scoped enums solve this problem:

enum class Suit { Diamonds, Hearts, Clubs, Spades };
 
enum class Tools { Picks, Shovels, Spades, Hammers };

Use of the keyword class or struct in the enum definition indicates that each enum type is unique and not comparable to other enum types. In addition to this advantage, you can also forward-declare them without specifying an underlying type (as you do with unscoped enums if you intend to forward-declare).

Access to the enumeration names is in the format identifier::name as shown in the following re-write of a previous example.

enum class Suit { Diamonds, Hearts, Clubs, Spades };
 
void test()
{
    Suit hand;
   
    hand = Clubs; // error C2065: 'Clubs' : undeclared identifier
    hand = Suit::Clubs;
   
    int account_num = 12345678;
    hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
    account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int'
}

The line hand = account_num; still causes the error we expect, as previously shown with unscoped enums, but now the implicit conversion to an integer we might expect in the next statement, account_num = Suit::Hearts;, is no longer allowed. This is the strongly typed behavior previously mentioned.

type is the underlying type of the enum. type can be any integral type, such as signed or unsigned versions of int, short, or long. bool or char is also allowed. Explicitly defining the underlying type is the only ISO Standard way to use forward declaration with unscoped enums.

See Also

Reference

C Enumeration Declarations

C++ Keywords