In C++, enumerations (enum
) fall into the category of constant types because they define a set of named integer constants.
enumerations
(or enums) provide a way to define a set of named integer constants, improving code readability and making it easier to work with a fixed set of values. They are especially useful when representing a collection of related values that do not change, such as days of the week, error codes, or options in a menu.
Types of Enumerations in C++
There are two main types of enumerations in C++:
- Traditional Enumeration (Unscoped)
- Scoped Enumeration (C++11)
1️⃣ Traditional Enumeration (Unscoped Enum)
In traditional (unscoped) enums, the enumerator names are placed in the global scope, which means they are accessible without qualifying them with the enum name. The underlying type for these enums is usually int
, and the values are implicitly assigned starting from 0 unless explicitly specified.
Syntax:
enum EnumName {
enumerator1, // Implicitly 0
enumerator2, // Implicitly 1
enumerator3 = 10, // Explicitly 10
enumerator4 // Implicitly 11
};
Example:
#include <iostream>
enum Color {
RED, // 0
GREEN, // 1
BLUE = 5, // 5
YELLOW // 6 (follows BLUE)
};
int main() {
Color color = GREEN;
std::cout << "Color value: " << color << std::endl; // Outputs: Color value: 1
return 0;
}
Key Points:
- Enumerators are automatically assigned integer values, starting from 0 by default.
- You can assign explicit values to specific enumerators, and the rest will increment from there.
- Enumerator names are in the global scope, so conflicts with other variables or enumerations are possible.
Limitations:
- No strong type-checking. Enumerators are essentially integers, so they can be mixed with other integers without any compiler warnings.
- Global scope pollution. Since enumerator names are placed in the global namespace, they can lead to name collisions.
2️⃣ Scoped Enumeration (C++11)
With the introduction of C++11, scoped enumerations (also known as enum
class) were added. Scoped enums
solve many of the limitations of traditional enums
by keeping enumerators within the scope of the enum
and providing better type safety.
Syntax:
enum class EnumName {
enumerator1,
enumerator2,
enumerator3 = 10
};
Example:
#include <iostream>
enum class TrafficLight {
RED,
GREEN,
YELLOW
};
int main() {
TrafficLight light = TrafficLight::GREEN;
if (light == TrafficLight::GREEN) {
std::cout << "The light is green!" << std::endl;
}
return 0;
}
Key Points:
- Enumerators are scoped within the enumeration, so they need to be qualified (e.g.,
TrafficLight::RED
). - Type safety is enforced. You cannot implicitly convert an enum class value to an integer, which prevents accidental misuse.
Comparison of Traditional and Scoped Enums
Feature | Traditional Enum (Unscoped) | Scoped Enum (enum class ) |
---|---|---|
Namespace pollution | Yes (global scope) | No (scoped within enum) |
Type safety | No | Yes |
Implicit conversions | Yes | No |
Underlying type | Usually int , but can be specified | Must be explicitly specified or defaults to int |
Enumerator naming | Simple names (e.g., RED ) | Must use EnumName::RED |
Underlying Type of Enums
In both traditional and scoped enumerations, you can specify the underlying type of the enumeration. By default, the underlying type is int
, but this can be changed to any integral type (such as char
, short
, long
, etc.).
Specifying Underlying Type in Traditional Enums
enum Color : unsigned int {
RED = 1,
GREEN = 2,
BLUE = 3
};
Specifying Underlying Type in Scoped Enums
enum class Color : char {
RED = 'r',
GREEN = 'g',
BLUE = 'b'
};
Operations with Enums
Conversion to Integer
Traditional enums can be implicitly converted to integers.
enum Color {
RED, GREEN, BLUE
};
int main() {
int colorValue = RED; // Implicit conversion
std::cout << "Color Value: " << colorValue << std::endl; // Outputs: 0
return 0;
}
For scoped enums (enum class
), you need to explicitly cast them to their underlying type:
enum class Color {
RED, GREEN, BLUE
};
int main() {
int colorValue = static_cast<int>(Color::RED);
std::cout << "Color Value: " << colorValue << std::endl; // Outputs: 0
return 0;
}
Comparing Enums
Both traditional and scoped enums can be compared directly.
if (Color::RED == Color::GREEN) { // Always false
// This block will not execute
}
However, scoped enums are strongly typed, meaning you cannot compare them with other enums or integers directly.
Bitwise Operations
Traditional enums allow bitwise operations (AND, OR, XOR), but scoped enums do not support bitwise operations unless you define them explicitly. For bitwise flags, traditional enums are often used with powers of two.
Bitmask Example:
enum Permission {
READ = 1 << 0, // 1
WRITE = 1 << 1, // 2
EXECUTE = 1 << 2 // 4
};
int main() {
int myPermissions = READ | WRITE;
if (myPermissions & READ) {
std::cout << "Read permission granted." << std::endl;
}
return 0;
}
For scoped enums, you'd need to define the bitwise operations manually, as they don’t support implicit bitwise operations.