Quick Syntax
서로 다른 상태 집합을 섞지 않으려면 scoped enum인 enum class를 씁니다. 열거자는 Type::member로 쓰며 정수로 암묵 변환되지 않습니다.
cpp
#include <iostream>
enum class Color { red, green, blue };
enum class State { idle, running, stopped };
int main() {
Color color = Color::green;
State state = State::running;
// color == state; // 컴파일 오류: 다른 enum class 타입
(void)color;
if (state == State::running) std::cout << "running\n";
}여러 flag를 동시에 조합해야 하면 enum 값 하나가 아니라 정수 mask 또는 별도 flag 설계를 검토합니다. 외부 정수 입력은 enum 값으로 cast하기 전에 허용 범위를 검증합니다.
문법
구식 enum의 문제 — 이름 누출과 암시적 변환
구식 enum은 열거자 이름이 바깥 스코프로 퍼져 충돌을 만들고, 정수와 암시적으로 비교되어 버그를 감춥니다.
cpp
enum Direction { up, down, left, right }; // 구식 enum
enum Status { ok, error, left }; // ❌ 컴파일 오류 — left 중복
// 구식 enum은 정수로 암시적 변환
Direction d = up;
int i = d; // ✅ 암시적으로 정수 0
if (d == 0) { ... } // ✅ 컴파일되지만 의미상 이상한 비교
// ✅ enum class — 이름 스코프 분리, 암시적 변환 없음
enum class Dir { up, down, left, right };
int j = Dir::up; // ❌ 컴파일 오류 — 암시적 변환 없음
if (Dir::up == 0) { ... } // ❌ 컴파일 오류기저 타입 지정 — 크기와 전방 선언
기저 타입을 지정하면 메모리 크기를 제어하고 전방 선언이 가능합니다.
cpp
// 기저 타입 지정 (기본은 int)
enum class Opcode : uint8_t { nop = 0, add = 1, sub = 2 };
// sizeof(Opcode) == 1
// scoped enum 전방 선언 — 기저 타입 생략 시 int
enum class Priority; // 헤더에서 전방 선언
enum class SmallPriority : uint8_t;
// 나중에 다른 파일에서 정의 가능
// 명시적 정수 변환
Opcode op = Opcode::add;
uint8_t raw = static_cast<uint8_t>(op); // 1플래그 enum — 비트 연산 패턴
여러 옵션을 비트 플래그로 조합할 때 enum class에 |, &, ~를 오버로딩합니다.
cpp
enum class Permission : uint8_t {
none = 0,
read = 1 << 0, // 0b001
write = 1 << 1, // 0b010
execute = 1 << 2, // 0b100
};
// 비트 연산자 오버로딩
Permission operator|(Permission a, Permission b) {
return static_cast<Permission>(
static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
}
Permission operator&(Permission a, Permission b) {
return static_cast<Permission>(
static_cast<uint8_t>(a) & static_cast<uint8_t>(b));
}
bool hasFlag(Permission set, Permission flag) {
return (set & flag) != Permission::none;
}
// 사용
Permission perm = Permission::read | Permission::write; // 0b011
bool canRead = hasFlag(perm, Permission::read); // true
bool canExec = hasFlag(perm, Permission::execute); // falseswitch와 enum class — 완전성 경고 활용
enum class의 열거자가 빠진 switch는 컴파일러 경고 옵션에 따라 경고를 낼 수 있습니다. -Wswitch 같은 경고를 켜고 default를 둘지 팀 정책을 정하면 새 열거자 추가 시 처리 누락을 검토할 수 있습니다.
cpp
enum class Direction { north, south, east, west };
void move(Direction d) {
switch (d) {
case Direction::north: goNorth(); break;
case Direction::south: goSouth(); break;
case Direction::east: goEast(); break;
// ⚠️ west 처리 없음 — 컴파일러 경고
// 새 열거자 추가 시 처리 누락을 컴파일러가 알려줌
}
}체크포인트
| 상황 | 적합한 선택 |
|---|---|
| 이름 충돌 방지 + 타입 안전성 | enum class |
| 정수 값 직접 필요 | static_cast<int>(val) |
| 크기 줄이기 (네트워크, 파일) | enum class Foo : uint8_t |
| 전방 선언 | scoped enum은 기본 int 또는 명시 기저 타입으로 선언 |
| 새 열거자 추가 시 처리 누락 감지 | switch + -Wswitch 같은 경고 설정 |
| 여러 옵션 조합 (비트 플래그) | 2의 거듭제곱 값 + |/& 오버로딩 |
주의할 점
enum class는 자동으로 문자열로 변환되지 않습니다. 로그나 출력이 필요하면 변환 함수를 따로 만들어야 합니다.
cpp
enum class Status { ok, error, timeout };
// ❌ 직접 출력 불가
Status s = Status::error;
std::cout << s; // 컴파일 오류
// ✅ 변환 함수 작성
std::string_view toString(Status s) {
switch (s) {
case Status::ok: return "ok";
case Status::error: return "error";
case Status::timeout: return "timeout";
}
return "unknown";
}
std::cout << toString(s); // "error"
// ⚠️ 구식 enum은 +1, -1 같은 정수 연산이 가능해 의도치 않은 버그 발생
enum OldColor { red, green, blue };
OldColor c = static_cast<OldColor>(red + 1); // green — 암묵적 허용참고 링크
1 sources