C++ Enum Tutorial: enum and enum class Explained
Imagine tracking the status of a network request. You could represent it as an integer: 0 means pending, 1 means success, 2 means failed. That works, but what happens six months later when someone reads if (status == 2)? They have no idea what 2 means without going back to find the definition.
Enums (enumerations) solve this by giving meaningful names to a set of related integer values. Instead of 2, you write Status::FAILED. That’s readable at a glance.
This tutorial covers C++ enums from scratch — including the modern enum class that you should be using in new code.
Your First enum
Here’s a basic enum:
#include <iostream>
using namespace std;
enum Direction {
NORTH,
SOUTH,
EAST,
WEST
};
int main() {
Direction d = NORTH;
if (d == NORTH) {
cout << "Heading north!" << endl;
}
return 0;
}
By default, NORTH = 0, SOUTH = 1, EAST = 2, WEST = 3. The compiler assigns integers starting from zero, incrementing by one.
You’ve replaced meaningless numbers with readable names. That’s the entire point.
Assigning Custom Values
You don’t have to accept the default 0, 1, 2, 3. You can assign any integer values you want:
enum HttpStatus {
OK = 200,
CREATED = 201,
NOT_FOUND = 404,
SERVER_ERROR = 500
};
int main() {
HttpStatus response = OK;
cout << "Status code: " << response << endl; // 200
return 0;
}
This is great for HTTP status codes, error codes, or any domain where specific numbers already have meaning.
You can also set some values and let the rest auto-increment:
enum Priority {
LOW = 1,
MEDIUM, // 2 (auto-incremented)
HIGH, // 3
CRITICAL = 10
};
The Problem with Plain enum
Plain enums have a significant issue: their values pollute the surrounding scope. Consider:
enum Color { RED, GREEN, BLUE };
enum Fruit { APPLE, ORANGE, GREEN }; // Error! GREEN is already defined
GREEN from Color clashes with GREEN from Fruit. This is called name leakage, and it’s a real problem in large codebases.
There’s another issue: plain enums implicitly convert to int. That means this compiles without a warning, even though it makes no sense:
enum Color { RED, GREEN, BLUE };
enum Size { SMALL, MEDIUM, LARGE };
Color c = RED;
int n = c; // Compiles fine — converts Color to int
bool same = (c == SMALL); // Compares RED (0) to SMALL (0) — true!
RED == SMALL evaluates to true because they’re both 0 under the hood. That’s a bug waiting to happen.
enum class: The Modern Solution
C++11 introduced enum class (also called scoped enumerations). It solves both problems:
#include <iostream>
using namespace std;
enum class Color {
RED,
GREEN,
BLUE
};
int main() {
Color c = Color::RED; // Must use the type prefix
if (c == Color::RED) {
cout << "Color is red" << endl;
}
return 0;
}
Two key differences from plain enum:
- Names are scoped: You write
Color::RED, not justRED. No naming conflicts. - No implicit int conversion:
int n = Color::RED;is a compile error. You have to cast explicitly.
Use enum class for all new code. It’s safer and clearer.
Using enum class in a switch
Enums and switch statements are a natural pair:
#include <iostream>
using namespace std;
enum class Season {
SPRING,
SUMMER,
AUTUMN,
WINTER
};
void describe(Season s) {
switch (s) {
case Season::SPRING: cout << "Flowers blooming" << endl; break;
case Season::SUMMER: cout << "Hot and sunny" << endl; break;
case Season::AUTUMN: cout << "Leaves falling" << endl; break;
case Season::WINTER: cout << "Cold and dark" << endl; break;
}
}
int main() {
describe(Season::AUTUMN); // Leaves falling
return 0;
}
A good compiler will warn you if you miss a case in the switch — another benefit of using enums over raw integers.
Converting enum class to int
Unlike plain enums, enum class won’t auto-convert to int. When you genuinely need the underlying integer value, use static_cast:
enum class Priority {
LOW = 1,
MEDIUM = 2,
HIGH = 3
};
int main() {
Priority p = Priority::HIGH;
int value = static_cast<int>(p); // 3
cout << "Priority level: " << value << endl;
return 0;
}
The explicit cast makes it obvious that you’re intentionally treating the enum as an integer, rather than it happening accidentally.
Specifying the Underlying Type
By default, enum values are stored as int. You can specify a different underlying type — useful for saving memory or ensuring a specific size:
enum class Direction : uint8_t {
NORTH,
SOUTH,
EAST,
WEST
};
uint8_t is an unsigned 8-bit integer (0–255), fine for four directions. You can use any integral type: int, unsigned int, short, long, uint8_t, etc.
enum vs #define vs const
Before enums existed, programmers used #define for named constants:
#define RED 0
#define GREEN 1
#define BLUE 2
Problems: #define is a text substitution done by the preprocessor before compilation. It has no type, no scope, and no safety. const int is better, but it still doesn’t group related values together.
Enums give you:
- Type safety — you can’t accidentally mix enums from different types (with
enum class) - Grouping — all related values live under one named type
- Debugger support — debuggers know the enum names, not just the numbers
A Practical Example: Game State Machine
Here’s a real-world pattern you’ll see often — using an enum to represent program state:
#include <iostream>
using namespace std;
enum class GameState {
MENU,
PLAYING,
PAUSED,
GAME_OVER
};
void update(GameState state) {
switch (state) {
case GameState::MENU: cout << "Showing main menu" << endl; break;
case GameState::PLAYING: cout << "Game running..." << endl; break;
case GameState::PAUSED: cout << "Game paused" << endl; break;
case GameState::GAME_OVER: cout << "Game over screen" << endl; break;
}
}
int main() {
GameState state = GameState::MENU;
update(state);
state = GameState::PLAYING;
update(state);
state = GameState::GAME_OVER;
update(state);
return 0;
}
The enum makes the state machine’s logic clear and self-documenting. Anyone reading this code immediately understands what’s going on.
Quick Reference
| Feature | plain enum | enum class |
|---|---|---|
| Name scoping | No (names leak) | Yes (prefix required) |
| Implicit int conversion | Yes | No |
| C++ version | All | C++11+ |
| Recommended? | Legacy code only | Yes, for new code |
Related Articles
- C++ Variables and Data Types — enums are a user-defined type built on top of integers
- C++ Switch Statement — the natural companion to enums
- C++ Conditionals Tutorial — if/else with enum conditions
- C++ Classes and Objects — enums are often used as class member variables
Take Your C++ Further
If you’re looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you’re a complete beginner or looking to solidify your understanding. Just $19.