Enums & enum class
An enum gives friendly names to a fixed set of related values — directions, days, states, status codes. Instead of scattering magic numbers like 0 , 1 , 2 through your code, you write Color::Red . This lesson covers plain enums, the safer modern enum class , custom values, and how to print them.
Learn Enums & enum class in our free C++ course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick recall.
Part of the free C++ course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
An enum is like the gear selector in a car: P, R, N, D. There's a small, fixed set of valid positions, each with a clear name. You'd never label them "0, 1, 2, 3" on the dashboard — the words prevent mistakes. A plain enum is like a loose lever that can slip into a number you didn't intend; an enum class is a precise gate that only lets you select a named, valid position. That extra safety is why modern C++ favors enum class .
1. Plain (Unscoped) Enums
The classic enum defines named constants that start at 0 and count up. They convert to int automatically and their names live in the surrounding scope. That convenience is also their weakness — names can clash and values get compared to raw numbers by accident.
2. The Safer enum class
A scoped enum keeps its names behind the type name ( Color::Red ) and refuses to convert to int without an explicit cast. This prevents accidental comparisons and name clashes, which is why it's the recommended default in modern C++.
3. Custom Underlying Values
You can assign explicit numbers to enum members — perfect for things like HTTP status codes or error codes that have meaningful values. Any members you leave unspecified keep counting up from the previous one.
4. Printing Enums with a switch
Since an enum stores a number, not a word, cout can't print a readable name on its own. The standard pattern is a small function that takes the enum and returns a std::string via a switch — clear, fast, and easy to extend.
5. Choosing the Underlying Type
By default an enum is backed by int . You can pin it to a smaller type — like unsigned char for a single byte — when memory matters or a binary format requires it. Converting a raw number back into the enum needs an explicit static_cast , and you should validate the number first.
These lines define a scoped enum and print one member as an int. Reorder them so the program prints 2 .
Why: the enum class must be declared before main can use it. Inside main you create the variable (B then A), then print it — and because enum class won't convert implicitly, you need the explicit static_cast<int> . Large is the third member (Small=0, Medium=1, Large=2), so it prints 2 .
Predict the output before revealing each answer.
2 — a plain enum converts to int automatically, and C is the third member (A=0, B=1, C=2).
7 — X is 5, so Y is 6 and Z is 7 (counting continues from the explicit value).
Compile error — an enum class has no implicit conversion to int, so cout can't print it without a static_cast .
Define an OrderStatus enum, map it to a label with a switch, and detect terminal states. Match the expected output in the comments.
Practice quiz
What is the main difference between a plain enum and an enum class?
- enum class keeps its names scoped and does not implicitly convert to int; a plain enum leaks names and converts to int automatically
- enum class cannot have custom values, but a plain enum can
- A plain enum is the modern, preferred form added in C++11
- There is no difference; they are interchangeable keywords
Answer: enum class keeps its names scoped and does not implicitly convert to int; a plain enum leaks names and converts to int automatically. A scoped enum class avoids name leaks and accidental int comparisons, which is why modern C++ prefers it.
Given enum class Color { Red, Green, Blue };, how do you print the value of c = Color::Green?
- cout << c;
- cout << static_cast<int>(c);
- cout << Color::c;
- cout << (int)Color;
Answer: cout << static_cast<int>(c);. An enum class has no implicit conversion to int, so you must cast: static_cast<int>(c). Printing it directly is a compile error.
For enum class Level { Low = 1, Medium, High };, what is static_cast<int>(Level::High)?
- 1
- 2
- 3
- 0
Answer: 3. Low is 1, so Medium auto-increments to 2 and High to 3.
What does cout << C; print for plain enum E { A, B, C };?
- A compile error
- 2
- C
- 3
Answer: 2. A plain enum converts to int automatically, and C is the third member (A=0, B=1, C=2).
How do you pin a scoped enum's storage to a single byte?
- enum class Suit : unsigned char { ... };
- enum class Suit byte { ... };
- enum Suit(1) { ... };
- You cannot change an enum's underlying type
Answer: enum class Suit : unsigned char { ... };. Specifying the underlying type, e.g. : unsigned char, pins the storage size (here 1 byte).
To access a member of enum class Color { Red, Green, Blue };, you must write:
- Red
- Color.Red
- Color::Red
- Color->Red
Answer: Color::Red. Scoped enum members must be qualified with the type name and ::, so Color::Red.
What is the standard way to turn an enum into a readable name?
- cout prints the name automatically
- A function with a switch that returns a std::string per case
- Calling .name() on the enum value
- Using static_cast<string>(value)
Answer: A function with a switch that returns a std::string per case. Since an enum stores a number, not text, you write a small function (usually a switch) that returns the right string.
For enum class HttpStatus { Ok = 200, Created, NotFound = 404 };, what is the value of Created?
- 200
- 201
- 1
- 404
Answer: 201. Unspecified values keep counting up from the previous one, so Created is 201.
Why must you validate a number before static_cast<Color>(n)?
- The cast will not compile otherwise
- An out-of-range value produces an enum with no matching name
- static_cast cannot convert int to an enum at all
- It automatically clamps to the nearest valid value
Answer: An out-of-range value produces an enum with no matching name. Casting an out-of-range int, like static_cast<Color>(99), compiles but yields an invalid enum value, so you must range-check first.
What is sizeof(Suit) for enum class Suit : unsigned char { Clubs, Diamonds, Hearts, Spades };?
- 1
- 2
- 4
- 8
Answer: 1. The underlying type is unsigned char, so the enum occupies exactly 1 byte.