Appearance
Enums and Strong Enums
New to this topic?
Enumerations (enums) are a way to define a set of named constants in C++. They provide a more readable alternative to using magic numbersmagic numbers are numbers that are used to represent something without a clear meaning. Say representing colors as numbers like 0 for red, 1 for green, 2 for blue., and help make code more maintainable. C++11 introduced "strong enums" (enum class) which provide additional type safety and scope control.
Traditional Enums (C-style)
Traditional enums are inherited from C and provide a simple way to define named constants.
Basic Syntax
cpp
enum Color {
Red, // 0
Green, // 1
Blue // 2
};
// Usage
Color favorite = Red;
Color sky = Blue;Explicit Values
You can assign explicit values to enum constants:
cpp
enum Status {
Pending = 0,
Running = 1,
Completed = 2,
Failed = -1
};
// Values can be any integer
enum Bits {
Read = 1, // 2^0
Write = 2, // 2^1
Execute = 4 // 2^2
};Underlying Type
By default, traditional enums have an underlying type of int:
cpp
enum SmallEnum {
A, B, C
};
// The underlying type is int
static_assert(std::is_same_v<std::underlying_type_t<SmallEnum>, int>);
// Size is typically 4 bytes (size of int)
std::cout << sizeof(SmallEnum) << std::endl; // Usually prints 4Implicit Conversion
Traditional enums can be implicitly converted to integers:
cpp
enum Direction {
North = 0,
South = 1,
East = 2,
West = 3
};
Direction dir = North;
int value = dir; // OK: value = 0
int sum = dir + 5; // OK: sum = 5
bool isNorth = (dir == 0); // OK: true
Direction dir = 5; // Fails!Scope Issues
Traditional enums pollute the global scope:
cpp
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green }; // Error: Red and Green already defined
// This can cause naming conflicts
int Red = 42; // Error: Red already definedStrong Enums (enum class)
enum class (also called "strong enums" or "scoped enums") provides better type safety and scope control.
Basic Syntax
cpp
enum class Color {
Red,
Green,
Blue
};
// Usage requires scope resolution
Color favorite = Color::Red;
Color sky = Color::Blue;Explicit Underlying Type
You can specify the underlying type explicitly:
cpp
enum class Status : uint8_t {
Pending = 0,
Running = 1,
Completed = 2,
Failed = 255
};
// Size is now 1 byte (size of uint8_t)
std::cout << sizeof(Status) << std::endl; // Prints 1No Implicit Conversion
Enum classes prevent implicit conversions to integers:
cpp
enum class Direction {
North = 0,
South = 1,
East = 2,
West = 3
};
Direction dir = Direction::North;
// int value = dir; // Error: no implicit conversion
// int sum = dir + 5; // Error: no implicit conversion
// bool isNorth = (dir == 0); // Error: no implicit conversion
// Explicit conversion is required
int value = static_cast<int>(dir); // OK: value = 0
int sum = static_cast<int>(dir) + 5; // OK: sum = 5
bool isNorth = (static_cast<int>(dir) == 0); // OK: trueScope Safety
Enum class values are contained within their scope:
cpp
enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green }; // OK: no conflicts
// No global scope pollution
int Red = 42; // OK: Red is not in global scopeComparison and Usage
When to Use Traditional Enums
Use traditional enums when:
- You need implicit conversion to integers
- You're working with legacy C code
- You need to use enum values in arithmetic operations
cpp
// Good use case: bit flags
enum Permissions {
Read = 1,
Write = 2,
Execute = 4
};
Permissions perms = Read | Write; // OK: implicit conversion
if (perms & Read) { // OK: bitwise operations
// User has read permission
}When to Use Enum Class
Use enum class when:
- You want type safety
- You need scope control
- You're writing new C++ code
- You want to prevent accidental conversions
cpp
// Good use case: type-safe state machine
enum class ConnectionState {
Disconnected,
Connecting,
Connected,
Error
};
ConnectionState state = ConnectionState::Disconnected;
// Type-safe comparisons
if (state == ConnectionState::Connected) {
// Handle connected state
}
// No accidental integer comparisons
// if (state == 2) { } // Error: prevents bugsAdvanced Features
Forward Declarations
Both enum types support forward declarations:
cpp
// Forward declaration
enum class Status;
// Later definition
enum class Status : int {
Pending,
Running,
Completed
};Enum as Class Members
Enums can be members of classes:
cpp
class NetworkConnection {
public:
enum class State {
Disconnected,
Connecting,
Connected,
Error
};
enum class Protocol {
HTTP,
HTTPS,
FTP
};
private:
State currentState;
Protocol protocol;
public:
void setState(State newState) {
currentState = newState;
}
State getState() const {
return currentState;
}
};
// Usage
NetworkConnection conn;
conn.setState(NetworkConnection::State::Connecting);Enum with Custom Operators
You can define custom operators for enums:
cpp
enum class Direction {
North, South, East, West
};
// Custom increment operator
Direction& operator++(Direction& dir) {
switch (dir) {
case Direction::North: return dir = Direction::East;
case Direction::East: return dir = Direction::South;
case Direction::South: return dir = Direction::West;
case Direction::West: return dir = Direction::North;
}
return dir;
}
// Usage
Direction dir = Direction::North;
++dir; // dir is now Direction::EastBest Practices
1. Prefer enum class for New Code
cpp
// Good: Strong enum
enum class Color { Red, Green, Blue };
// Avoid: Traditional enum (unless you need implicit conversion)
enum Color { Red, Green, Blue };2. Use Descriptive Names
cpp
// Good: Clear and descriptive
enum class HttpStatus {
Ok = 200,
NotFound = 404,
InternalServerError = 500
};
// Avoid: Unclear names
enum class E { A, B, C };3. Specify Underlying Type When Needed
cpp
// Good: Explicit underlying type for bit flags
enum class Permissions : uint32_t {
Read = 1U << 0,
Write = 1U << 1,
Execute = 1U << 2
};
// Good: Small underlying type for limited values
enum class Month : uint8_t {
January = 1, February, March, April, May, June,
July, August, September, October, November, December
};4. Use enum class for Compile-time Values
cpp
enum class Constants : int {
MaxSize = 1000,
Timeout = 5000
};
// These values are available at compile time
static_assert(static_cast<int>(Constants::MaxSize) > 0);Questions
Q: What is the main advantage of enum class over traditional enums?
Enum classes provide scope safety by keeping enum values within their scope, and prevent implicit conversions to integers, making code safer and more maintainable.
Q: What is the underlying type of a traditional enum by default?
Traditional enums have an underlying type of int by default, which means they can store values from INT_MIN to INT_MAX.
Q: How do you access values from an enum class?
Enum class values must be accessed with scope resolution operator (:😃, like Color::Red, which provides scope safety and prevents naming conflicts.
Q: What happens when you try to assign an integer to an enum class variable?
Enum classes prevent implicit conversions from integers, so assigning an integer directly to an enum class variable will cause a compilation error, requiring explicit casting.
Q: When should you prefer traditional enums over enum class?
Traditional enums allow implicit conversion to integers, making them useful when you need to use enum values in arithmetic operations or comparisons with integers.