한글 번역
title: “이번 주의 팁 #86: 클래스(enum class)를 활용한 열거형” layout: tips sidenav: side-nav-tips.html published: true permalink: tips/86 type: markdown order: “086” —
원래 totw/86로 2015-01-05에 게시됨
작성자: Bradley White (bww@google.com)
“품격을 보여주고, … 그리고 캐릭터를 드러내라.” - Bear Bryant
열거형(enumeration) 또는 줄여서 enum은 특정 정수 집합 중 하나의 값을 가질 수 있는 타입입니다. 이 집합의 일부 값에 이름을 부여할 수 있으며, 이를 열거자(enumerator)라고 부릅니다.
비스코프 열거형 (Unscoped Enumerations)
이 개념은 C++ 프로그래머에게 익숙할 것입니다. 그러나 C++11 이전의 열거형에는 두 가지 큰 단점이 있었습니다:
- 열거자 이름이 열거형 타입과 동일한 스코프에 존재했습니다.
- 열거자는 암묵적으로 정수형 타입으로 변환되었습니다.
C++98에서의 예시:
enum CursorDirection { kLeft, kRight, kUp, kDown };
CursorDirection d = kLeft; // 가능: 열거자가 같은 스코프에 있음
int i = kRight; // 가능: 열거자가 정수형으로 변환됨
그러나 다음과 같은 경우에는 문제가 발생합니다:
// 오류: kLeft와 kRight가 중복 선언됨
enum PoliticalOrientation { kLeft, kCenter, kRight };
C++11에서의 변경 사항
C++11에서는 비스코프 열거형의 동작을 약간 변경했습니다. 열거자는 이제 열거형 내부에 로컬로 존재하지만, 이전 버전과의 호환성을 위해 여전히 해당 열거형의 스코프로 내보내집니다.
CursorDirection d = CursorDirection::kLeft; // C++11에서 가능
int i = CursorDirection::kRight; // 가능: 여전히 정수형으로 변환됨
하지만 PoliticalOrientation
의 선언은 여전히 오류를 발생시킵니다.
스코프 열거형 (Scoped Enumerations)
암묵적인 정수형 변환은 흔히 발생하는 버그의 원인이 되며, 열거자가 같은 스코프에 존재함으로 인해 네임스페이스 오염이 발생해 대규모 프로젝트에서는 문제가 될 수 있습니다. 이러한 문제를 해결하기 위해, C++11은 새로운 개념인 스코프 열거형(scoped enum)을 도입했습니다.
스코프 열거형에서는 enum class
키워드를 사용하며, 이 경우 열거자는:
- 해당 열거형에만 로컬로 존재하며 (열거형의 스코프로 내보내지 않음),
- 정수형 타입으로 암묵적으로 변환되지 않습니다.
예시 (추가된 class
키워드 주목):
enum class CursorDirection { kLeft, kRight, kUp, kDown };
CursorDirection d = kLeft; // 오류: kLeft가 이 스코프에 없음
CursorDirection d2 = CursorDirection::kLeft; // 가능
int i = CursorDirection::kRight; // 오류: 변환 불가
그리고 다음과 같이:
// 가능: kLeft와 kRight는 각각의 스코프 열거형에 로컬로 존재함
enum class PoliticalOrientation { kLeft, kCenter, kRight };
이러한 간단한 변경으로 기존의 열거형 문제를 해결할 수 있으며, 새로운 코드에서는 enum class
를 사용하는 것이 권장됩니다.
스코프 열거형을 사용하면 여전히 정수형 타입으로 변환이 필요할 때는 명시적으로 캐스팅해야 합니다 (예: 열거형 값을 로그에 출력하거나 플래그와 같은 열거자에 비트 연산을 사용하는 경우). 그러나 std::hash
를 사용하는 해싱 작업(std::unordered_map<CursorDirection, int>
등)에는 여전히 문제가 없습니다.
열거형의 기본 타입 지정
C++11은 또한 두 종류의 열거형 모두에 대해 기본 타입을 지정할 수 있는 기능을 추가했습니다. 이전에는 열거형의 기본 정수형 타입이 열거자들의 부호와 크기를 보고 결정되었으나, 이제는 명시적으로 지정할 수 있습니다.
예시:
// CursorDirection의 기본 타입을 "int"로 사용
enum class CursorDirection : int { kLeft, kRight, kUp, kDown };
열거자 범위가 작고, CursorDirection
값을 저장할 때 메모리를 절약하고 싶다면 char
로 지정할 수도 있습니다.
// CursorDirection의 기본 타입을 "char"로 사용
enum class CursorDirection : char { kLeft, kRight, kUp, kDown };
컴파일러는 열거자 값이 기본 타입의 범위를 초과하면 오류를 발생시킵니다.
결론
새로운 코드에서는 enum class
를 사용하는 것이 좋습니다. 이렇게 하면 네임스페이스 오염을 줄일 수 있고, 암묵적인 변환에서 발생할 수 있는 버그를 피할 수 있습니다.
enum class Parting { kSoLong, kFarewell, kAufWiedersehen, kAdieu };
reference
https://github.com/abseil/abseil.github.io/blob/master/_posts/2017-10-26-totw-86.md