개요
안녕하세요. 이번 글에서는 C++11에서 추가된 enum class(범위 있는 열거형, scoped enum)에 대해 알아보겠습니다. 기존 enum은 편리하지만 이름 충돌과 의도치 않은 정수 변환이라는 약점이 있습니다. enum class는 이 두 문제를 해결합니다.
기존 enum의 문제
먼저 전통적인 enum을 보겠습니다. 두 가지 불편한 점이 있습니다.
enum Color { Red, Green, Blue };
enum Fruit { Apple, Red }; // 에러! Red가 이미 있음 (이름 충돌)
enum Color { Red, Green, Blue };
int x = Red; // 문제 1: enum 값이 그냥 int로 들어감 (0)
if (Red == 0) { } // 문제 2: 정수와 바로 비교돼 버림
- 이름 충돌: enum 안의 이름이 바깥 범위로 그대로 노출돼, 다른 enum과 같은 이름을 못 씀.
- 암묵적 정수 변환: 열거형 값이 아무 제약 없이 int로 변환돼, 실수로 숫자와 섞여도 컴파일됨.
enum class — 범위 있는 열거형
enum 뒤에 class를 붙이면 됩니다. 이름이 열거형 안으로 갇히기 때문에, 사용할 때 열거형이름::값 형태로 적어야 합니다.
enum class Color { Red, Green, Blue };
enum class Fruit { Apple, Red }; // OK! 서로 범위가 달라 충돌 없음
Color c = Color::Red; // 반드시 Color:: 를 붙임
// Color c = Red; // 에러: Red만으로는 못 찾음
// int x = c; // 에러: 암묵적 정수 변환 금지
int x = static_cast<int>(c); // 변환하려면 명시적으로
이렇게 하면 이름 충돌이 사라지고, 정수로 바꾸려면 반드시 static_cast를 쓰도록 강제되어 실수가 줄어듭니다.
기존 enum vs enum class
| 구분 | enum (기존) | enum class |
|---|---|---|
| 이름 범위 | 바깥으로 노출 | 열거형 안으로 한정 (이름::값) |
| 정수 변환 | 암묵적으로 변환됨 | 금지 (static_cast 필요) |
| 이름 충돌 | 잘 발생 | 범위가 달라 안전 |
| 권장 | 레거시 코드 | 새 코드는 enum class 권장 |
기본 타입 지정
열거형이 내부적으로 사용할 정수 타입을 지정할 수 있습니다. 값의 범위가 작으면 메모리를 아끼거나, 통신 프로토콜에서 크기를 맞출 때 유용합니다.
// 각 값을 1바이트(unsigned char)로 저장
enum class Direction : unsigned char
{
North = 0,
East = 1,
South = 2,
West = 3
};
Direction d = Direction::East;
: 타입을 생략하면 기본은 int입니다. enum class뿐 아니라 기존 enum에도 이 문법을 쓸 수 있습니다.
switch와 함께 쓰기
열거형은 switch문과 자주 함께 쓰입니다. enum class도 동일하게 쓰되, case에도 열거형이름::값을 적습니다.
Color c = Color::Green;
switch (c)
{
case Color::Red: std::cout << "빨강"; break;
case Color::Green: std::cout << "초록"; break;
case Color::Blue: std::cout << "파랑"; break;
}
정리
- 기존 enum은 이름 충돌과 암묵적 정수 변환이라는 약점이 있다.
- enum class는 이름을 범위 안에 가두고(이름::값), 정수 변환을 막아 더 안전하다.
- 정수로 바꾸려면 static_cast를 명시적으로 쓴다.
- : 타입으로 내부 저장 타입을 지정할 수 있다.
- 특별한 이유가 없으면 새 코드에서는 enum class를 쓰는 것이 권장된다.
enum class는 약간 더 길게 적어야 하지만, 이름 충돌과 타입 실수를 컴파일 단계에서 막아 줍니다. 이상으로 enum class에 대해 간단히 알아보았습니다.