8.0 What This Teaches
- Plain
enumand its scope pollution problem - Scoped
enum class(preferred in modern C++) - Specifying the underlying integer type
- Using enums in
switchstatements - Converting between enums and integers
8.1 Plain enum
enum Direction { North, South, East, West };
Direction d = North; // North is in the enclosing scope
int n = d; // implicit conversion to int (North = 0)
enum names leak into the enclosing scope. If two enums
define Red, they conflict. The implicit conversion to int
is also a common source of bugs.
8.2 enum class (Scoped Enum)
enum class Color { Red, Green, Blue };
Color c = Color::Green; // must qualify with Color::
// int n = c; // error: no implicit conversion
int n = static_cast<int>(c); // 1 - explicit cast required
enum class keeps names in the enum's own scope and disables
implicit conversion to int. Prefer enum class in
new code.
8.3 Underlying Integer Type
enum class Status : uint8_t { Ok = 0, NotFound = 1, Error = 2 };
enum class Flags : uint32_t { None = 0, Read = 1, Write = 2, Exec = 4 };
int. Specifying it explicitly
controls storage size and enables bitwise flag operations on
Flags-style enums.
8.4 Enums in switch
std::string color_name(Color c) {
switch (c) {
case Color::Red: return "Red";
case Color::Green: return "Green";
case Color::Blue: return "Blue";
}
return "Unknown";
}
switch on an enum class omits
a case. This makes exhaustive handling easier to enforce than with integers.
8.5 Example - All Together
// Enums - plain enum, enum class, underlying type, switch.
#include <iostream>
#include <string>
enum Direction { North, South, East, West };
enum class Color { Red, Green, Blue };
enum class Status : uint8_t { Ok = 0, NotFound = 1, Error = 2 };
std::string direction_name(Direction d) {
switch (d) {
case North: return "North";
case South: return "South";
case East: return "East";
case West: return "West";
}
return "Unknown";
}
std::string color_name(Color c) {
switch (c) {
case Color::Red: return "Red";
case Color::Green: return "Green";
case Color::Blue: return "Blue";
}
return "Unknown";
}
int main() {
Direction d = North;
std::cout << direction_name(d) << " (" << d << ")\n";
Color c = Color::Green;
std::cout << color_name(c) << "\n";
Status s = Status::NotFound;
std::cout << "status: " << static_cast<int>(s) << "\n";
return 0;
}
North (0)
Green
status: 1
8.6 Exercise
Exercise
- Define an
enum class Weekdaywith valuesMonthroughSun. Write a functionis_weekend(Weekday d)that returnstrueforSatandSun. - Define an
enum class Permission : uint8_twithNone=0,Read=1,Write=2,Exec=4. Use bitwise OR to combine permissions and check them with bitwise AND. - Add a
default:case to aswitchon yourWeekdayenum that prints an error, then observe the compiler warning about the unhandled cases when you also have explicit cases for all values.
8.7 Common Mistakes
Name collision with plain enum
enum Fruit { Apple, Orange };
enum Color { Red, Orange }; // error: Orange already defined
enum class to put names in their own scope.Comparing enum class to integer without cast
Color c = Color::Red;
if (c == 0) { } // error: no implicit conversion from int to Color
static_cast<Color>(0) or compare against a named
enum value.Missing case in switch on enum class
enum class, the compiler warns
about switch statements that don't handle it - but only if you
compile with -Wall and do not have a default: case.
Omit default: on exhaustive enums to get this safety check.
8.8 Key Terms
| Term | Meaning |
|---|---|
| enum | Plain enumeration; names leak into enclosing scope; implicit int conversion |
| enum class | Scoped enumeration; names qualified by type; no implicit int conversion |
| underlying type | The integer type used to store enum values; default is int |
| static_cast<int>(e) | Explicit conversion from enum class to integer |
| exhaustive switch | A switch that handles every enumerator; compiler warns if a case is missing |