Home Uncategorized C++: Enumerations

C++: Enumerations

by wforbes

Enumerations are a data type that can be used to define a small set of constant values. That data type can then be used to create variables that restrict the value they can hold to ones that match a member of the set of values defined within its Enum data type. Enumerations are useful when creating State Machines, using flags, or any other situation where a variable should only have one of a few specific values.

In this post, I will show a few basic examples that demonstrate how to create Enumerations and some of their behaviors. Once this material is understood, I encourage you to search the internet to see more advanced examples of this useful and often overlooked topic in C++.

Declaring, Initializing, and Referencing an Enum

//declaring an enumeration of type 'items'
enum items { ITEM1, ITEM2, ITEM3, ITEM4 };

int main()
{
  //declaring a variable of type enum items
  enum items item;

  //initializing an enum variable with a reference to an enum item
  item = ITEM1;

  return 0;
}

An Enumeration can also be declared as a variable and initialized in a single statement:

int main() 
{
  // declaring an enum type 'friends'
  //  as a variable named 'friend'
  //  initialized to the 'FRED' value
  enum friends { FRED, ETHEL, LUCY, RICKY } friend = FRED;

  return 0;
}

Protecting Against Improper Initialization

Using an Enumeration ensures that only items within the defined set can be stored in the variable.

enum friends { WILMA, FRED, BETTY, BARNEY };

int main()
{
  enum friends friend;
  
  // This causes an error because JIM wasn't defined in the friends enum
  friend = DINO;

  return 0;
}

There are many things in life that can be represented this way. Sets like the seven days of the week [MON, TUES, WED, THURS, FRI, SAT, SUN]; the three colors of a traffic light [GREEN, YELLOW, RED]; the suits in a deck of playing cards [HEART, CLUB, DIAMOND, SPADE]; navigational directions [NORTH, SOUTH, EAST, WEST]; the seasons [ WINTER, SPRING, SUMMER, FALL ] and many more.

enum daysOfWeek { MON, TUES, WED, THURS, FRI, SAT, SUN};

enum trafficLightStates { GREEN, YELLOW, RED };

enum cardSuits { HEART, CLUB, DIAMOND, SPADE };

enum directions { NORTH, SOUTH, EAST, WEST };

enum seasons { WINTER, SPRING, SUMMER, FALL };

Enum Item Values

By default, the values in an Enumeration are set to integers, starting with 0 and incrementing with each additional item. For example, here the days of the week each have an integer value ranging from 0 to 6. The values can be iterated through and used in conditionals.

#include <iostream>
using namespace std;

enum daysOfWeek { MON, TUES, WED, THURS, FRI, SAT, SUN};

int main()
{
  // initialize a variable with a specific day in the week
  enum daysOfWeek humpDay = WED;

  for (int i = MON; i <= SUN; i++) { // iterate through the values of MON to SUN
    cout << i; // output each day's integer value

    // use a reference an item in the enum in a conditional
    if(i == TUES) {
      cout << " (Taco Tuesday!)";
    }

    // reference the value of the variable in a conditional
    if(i == humpDay) {
      cout << " (Hump Day!)";
    }

    cout << endl;
  }
  return 0;
}

Output:

0
1 (Taco Tuesday!)
2 (Hump Day!)
3
4
5
6

Custom Item Values

Instead of using these default values (0, 1, 2, …), they can instead be set to custom values which can be used for additional logic. Let’s look at an example where an enum represents a list of T.V. stations and the enum values are the T.V. channel numbers for each station.

#include <iostream>
using namespace std;

int main()
{
  enum tvStations { ABC = 5, FOX = 11, NBC = 2, CSPAN = 23, ESPN = 45 };

  cout << "TV Stations and Channels:" << endl;
  cout << "ABC: " << ABC << endl;
  cout << "FOX: " << FOX << endl;
  cout << "NBC: " << NBC << endl;
  cout << "CSPAN: " << CSPAN << endl;
  cout << "ESPN: " << ESPN << endl;

  return 0;
}

This can help remove the use of ‘magic numbers’ in your code, where a seemingly arbitrary integer literal is used and difficult to keep updated. You don’t have to remember the specific channel numbers for each station this way, and if the channel numbers change they only need to be updated in the enum declaration.

Using Default and Custom Values

If some enumeration items are set to a custom value while others aren’t, the ones that aren’t will revert to the default setting of simply incrementing the previous value. This means you can start the item values at a custom value and the proceeding items take the default incrementing values, continuing from the custom value that was set.

#include <iostream>
using namespace std;

int main()
{
  enum months { JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEPT, OCT, NOV, DEC };
  
  for (int i = JAN; i <= DEC; i++) {
    cout << i << endl;
  }

  return 0;
}

Output:

1
2
3
4
5
6
7
8
9
10
11
12

Scoped Enumeration References

What happens when you have two enumerations that both share one or more items and you need to reference one of those items? Simply using the item’s name will result in an error because the compiler doesn’t know which enumeration you intend to target with your reference. In a case like this, it’s useful to know you can reference an item in a specific enumeration using the scope resolution operator “::“. The enums just need to be declared with the class or struct keyword to provide them with a scope that the operator can resolve to.

#include <iostream>
using namespace std;

int main()
{
  enum class eastTVStations { ABC = 5, FOX = 11, NBC = 2 };
  enum struct westTVStations { ABC = 2, FOX = 7, NBC = 4 };

  // This results in an error because ABC exists in both enums
  // eastTVStations tvStation = ABC; 

  // Using the :: scope resolution operator to specify which enum
  eastTVStations tvStation1 = eastTVStations::ABC;
  westTVStations tvStation2 = westTVStations::ABC;

  return 0;
}

Type Casting a Scoped Enumeration

Although in these examples we’ve only put the enumerations item’s value into a variable typed by our enumeration, we can also put it into a variable of type int. However, when using a scoped enumeration with the class or struct keyword, this isn’t the case. Scoped enumerations are strongly-typed. This isn’t must be done by typecasting the scoped enumeration to our desired variable type, in this case, an int.

#include <iostream>
using namespace std;

int main()
{
  enum smurfs { PAPA, SMURFETTE, BRAINY, CLUMSY, HANDY, VANITY };
  int papa = PAPA; // This works just fine

  enum class jetsons { GEORGE, JANE, JUDY, ELROY };
  //int boy = jetsons::ELROY; // This causes an error
  int boy = static_cast<int>(jetsons::ELROY); // This works just fine

  return 0;
}

Specifying the Underlying Type

So far we’ve only seen examples where the underlying type (the type of the enums items values) is int. For the most part, this was all that the C and early versions of C++ can do. However, C++11 allows you to specify the underlying type of an enum explicitly. Observe this example with caution, though. Generally, it’s a good practice to let the compiler assign the underlying type unless you have a very good reason to use something other than an int.

#include <iostream>
using namespace std;

int main()
{
  enum directions: char { NORTH = 'N', SOUTH = 'S', EAST = 'E', WEST = 'W' };

  directions currentDir = SOUTH;

  cout << "Current Direction: " << currentDir << endl;

  return 0;
}

Output:

Current Direction: S

Summary

  • Enumerations are a good way to specify a small set of values, then restrict variables to only hold one of those values.
  • There are countless real-life examples that can be modeled with an enumeration, so it’s important to understand how they work and be able to use them when the opportunity arises.
  • By default, the values of the items in an enumeration are consecutive integers starting at 0 but it’s possible to assign them with custom values to suit your needs.
  • If both custom and default values are used, the default values will be the next consecutive integer from the last one.
  • When two enumerations contain the same item names, you can use scoped enumerations with the scope resolution operator to differentiate between them.
  • Normally enumerations can be put into variables of type int if necessary, but if a scoped enumeration is used the value must be type-cast if it is to be put into an int variable.
  • Integers are the default type of items in an enumeration, but with C++11 it’s possible to declare an enumeration to contain values other than int – although this should only be used when completely necessary.

Thanks for reading this post and I hope you picked up some useful information from it! Have a great day!

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.