Enumeration declaration
열거형 은 값이 특정 범위로 제한되는(자세한 내용은 아래 참조) 고유한 타입으로, 여러 개의 명시적으로 이름이 지정된 상수(" 열거자 ")를 포함할 수 있습니다.
상수들의 값은 열거형의 기반 타입 으로 알려진 정수형 타입의 값입니다. 열거형은 동일한 크기 , 값 표현 , 그리고 정렬 요구사항 을 기반 타입과 공유합니다. 또한, 열거형의 각 값은 기반 타입의 해당 값과 동일한 표현을 가집니다.
열거형은 다음 구문을 사용하여 (재)선언됩니다:
enum-key
attr
(선택 사항)
enum-head-name
(선택 사항)
enum-base
(선택 사항)
{
enumerator-list
(선택 사항)
}
|
(1) | ||||||||
enum-key
attr
(선택 사항)
enum-head-name
(선택 사항)
enum-base
(선택 사항)
{
enumerator-list
, }
|
(2) | ||||||||
enum-key
attr
(선택 사항)
enum-head-name
enum-base
(선택 사항)
;
|
(3) | (C++11 이후) | |||||||
| enum-key | - |
|
||||
| attr | - | (C++11 이후) 임의 개수의 속성 의 선택적 시퀀스 | ||||
| enum-head-name | - |
|
||||
| enum-base | - |
(C++11 이후)
콜론(
:
), 뒤에 이 열거형 타입의 고정된 기반 타입으로 사용될 정수 타입을 명시하는
type-specifier-seq
(cv-한정자가 있는 경우 한정자는 무시됨)
|
||||
| enumerator-list | - |
열거자 정의의 쉼표로 구분된 목록. 각 정의는 단순한 고유
identifier
(열거자의 이름이 됨)이거나 상수 표현식이 있는 고유 식별자:
identifier
=
constant-expression
.
두 경우 모두
identifier
바로 뒤에 선택적
속성 지정자 시퀀스
가 올 수 있음.
(C++17 이후)
|
두 가지 구별되는 종류의 열거형이 있습니다:
비한정 열거형
(
enum-key
enum
로 선언)과
한정 열거형
(
enum-key
enum class
또는
enum struct
로 선언).
목차 |
범위 없는 열거형
enum
이름
(선택사항)
{
열거자
=
상수-표현식
,
열거자
=
상수-표현식
,
...
}
|
(1) | ||||||||
enum
이름
(선택사항)
:
타입
{
열거자
=
상수-표현식
,
열거자
=
상수-표현식
,
...
}
|
(2) | (C++11 이후) | |||||||
enum
이름
:
타입
;
|
(3) | (C++11 이후) | |||||||
각 enumerator 는 열거형의 타입(즉, name )을 가지는 명명된 상수가 되어, 둘러싸는 스코프에서 보이며 상수가 필요한 모든 경우에 사용할 수 있습니다.
각 열거자는 기본 타입의 값과 연관됩니다.
=
이
enumerator-list
에 제공될 때, 열거자의 값은 해당
constant-expression
들과 연관된 값으로 정의됩니다. 첫 번째 열거자에
=
이 없으면 연관된 값은 0입니다. 정의에
=
이 없는 다른 열거자의 경우, 연관된 값은 이전 열거자의 값에 1을 더한 값입니다.
enum Foo { a, b, c = 10, d, e = 1, f, g = f + c }; //a = 0, b = 1, c = 10, d = 11, e = 1, f = 2, g = 12
비범위 열거형의 이름 은 생략될 수 있습니다: 이러한 선언은 열거자들을 둘러싼 범위로만 도입합니다:
enum { a, b, c = 0, d = a + 2 }; // a = 0, b = 1, c = 0, d = 2를 정의함
범위가 지정되지 않은 열거형이 클래스 멤버일 때, 해당 열거자들은 클래스 멤버 접근 연산자
.
와
->
를 사용하여 접근할 수 있습니다:
struct X { enum direction { left = 'l', right = 'r' }; }; X x; X* p = &x; int a = X::direction::left; // C++11 이상에서만 허용됨 int b = X::left; int c = x.left; int d = p->left;
항상 열거형 선언의 일부로 파싱됩니다: struct S { enum E1 : int {}; enum E1 : int {}; // 오류: 열거형 재선언, // enum E1 타입의 길이 0 비트 필드로 파싱되지 않음 }; enum E2 { e1 }; void f() { false ? new enum E2 : int(); // OK: 'int'가 기반 타입으로 파싱되지 않음 } |
(C++11부터) |
링크 목적을 위한 열거형 이름
typedef 이름을 링크 목적으로 가지고 있지 않고 열거자를 가지고 있는 이름 없는 열거형은 링크 목적의 typedef 이름 을 가지지 않으며, 링크 목적 으로는 그 기반 타입과 첫 번째 열거자로 표시됩니다; 이러한 열거형은 링크 목적의 이름 으로서 열거자를 가진다고 합니다.
범위가 지정된 열거형
1)
범위가 지정된 열거형 타입을 선언하며, 기본 타입은
int
입니다 (
class
와
struct
키워드는 완전히 동일합니다)
2)
범위가 지정된 열거형 타입을 선언하며, 기본 타입은
type
입니다
3)
기본 타입이
int
인 범위가 지정된 열거형에 대한 불투명 선언입니다
4)
기본 타입이
type
인 범위가 지정된 열거형에 대한 불투명 선언입니다
각
enumerator
는 열거형 타입(즉,
name
)의 명명된 상수가 되며, 열거형의 범위 내에 포함되고 범위 확인 연산자를 사용하여 접근할 수 있습니다. 범위가 지정된 열거자 값에서 정수 타입으로의 암시적 변환은 없지만,
이 코드 실행
#include <iostream> int main() { enum class Color { red, green = 20, blue }; Color r = Color::blue; switch(r) { case Color::red : std::cout << "red\n"; break; case Color::green: std::cout << "green\n"; break; case Color::blue : std::cout << "blue\n"; break; } // int n = r; // error: no implicit conversion from scoped enum to int int n = static_cast<int>(r); // OK, n = 21 std::cout << n << '\n'; // prints 21 } |
(C++11부터) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
열거형은 다음과 같은 모든 조건을 만족할 때 캐스트 없이 정수로부터 초기화될 수 있으며, 목록 초기화 를 사용합니다:
이는 새로운 정수 타입(예:
enum byte : unsigned char {}; // byte는 새로운 정수 타입; std::byte (C++17)도 참조 byte b{42}; // C++17부터 OK (직접-목록-초기화) byte c = {42}; // error byte d = byte{42}; // C++17부터 OK; b와 같은 값 byte e{-1}; // error struct A { byte b; }; A a1 = {{42}}; // error (생성자 매개변수의 복사-목록-초기화) A a2 = {byte{42}}; // C++17부터 OK void f(byte); f({42}); // error (함수 매개변수의 복사-목록-초기화) enum class Handle : std::uint32_t { Invalid = 0 }; Handle h{42}; // C++17부터 OK |
(C++17부터) |
using enum 선언
enum E { x }; void f() { int E; using enum E; // OK } using F = E; using enum F; // OK template<class T> using EE = T; void g() { using enum EE<E>; // OK } using enum 선언은 명명된 열거형의 열거자 이름들을 각 열거자에 대한 using 선언 을 통해 도입하는 것처럼 도입합니다. 클래스 범위에서 using enum 선언은 명명된 열거형의 열거자들을 멤버로 범위에 추가하여 멤버 검색에서 접근 가능하게 만듭니다. enum class fruit { orange, apple }; struct S { using enum fruit; // OK: orange와 apple을 S로 도입 }; void f() { S s; s.orange; // OK: fruit::orange를 지칭 S::orange; // OK: fruit::orange를 지칭 } 동일한 이름의 두 열거자를 도입하는 두 개의 using enum 선언은 충돌합니다. enum class fruit { orange, apple }; enum class color { red, orange }; void f() { using enum fruit; // OK // using enum color; // 오류: color::orange와 fruit::orange가 충돌 } |
(C++20부터) | ||||||||||||||||||||||||||
참고 사항
범위가 없는 열거형 타입의 값들은 승격(promoted) 되거나 변환(converted) 되어 정수 타입으로 변환될 수 있습니다:
enum color { red, yellow, green = 20, blue }; color col = red; int n = blue; // n == 21
정수, 부동소수점, 열거형 타입의 값들은
static_cast
를 사용하여 어떤 열거형 타입으로든 변환될 수 있습니다. 이러한 변환 후의 값이 반드시 열거형에 정의된 명명된 열거자 중 하나와 일치할 필요는 없습니다:
enum access_t { read = 1, write = 2, exec = 4 }; // 열거자: 1, 2, 4 범위: 0..7 access_t rwe = static_cast<access_t>(7); assert((rwe & read) && (rwe & write) && (rwe & exec)); access_t x = static_cast<access_t>(8.0); // CWG 1766 이후 정의되지 않은 동작 access_t y = static_cast<access_t>(8); // CWG 1766 이후 정의되지 않은 동작 enum foo { a = 0, b = UINT_MAX }; // 범위: [0, UINT_MAX] foo x = foo(-1); // CWG 1766 이후 정의되지 않은 동작, // foo의 기본 타입이 unsigned int인 경우에도
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_enumerator_attributes
|
201411L
|
(C++17) | 열거자 속성 |
__cpp_using_enum
|
201907L
|
(C++20) |
using enum
|
키워드
예제
#include <cstdint> #include <iostream> // 16비트를 사용하는 열거형 enum smallenum: std::int16_t { a, b, c }; // color는 red(값 0), yellow(값 1), green(값 20), blue(값 21) 중 하나일 수 있음 enum color { red, yellow, green = 20, blue }; // altitude는 altitude::high 또는 altitude::low일 수 있음 enum class altitude: char { high = 'h', low = 'l', // 후행 쉼표는 CWG 518 이후로만 허용됨 }; // 상수 d는 0, 상수 e는 1, 상수 f는 3 enum { d, e, f = e + 2 }; // 열거형 타입(범위 있는 것과 없는 것 모두)은 연산자 오버로딩을 가질 수 있음 std::ostream& operator<<(std::ostream& os, color c) { switch(c) { case red : os << "red"; break; case yellow: os << "yellow"; break; case green : os << "green"; break; case blue : os << "blue"; break; default : os.setstate(std::ios_base::failbit); } return os; } std::ostream& operator<<(std::ostream& os, altitude al) { return os << static_cast<char>(al); } // 범위 있는 열거형(C++11)은 이전 C++ 개정판에서 부분적으로 에뮬레이션될 수 있음: enum struct E11 { x, y }; // C++11 이후 struct E98 { enum { x, y }; }; // C++11 이전에서 OK namespace N98 { enum { x, y }; } // C++11 이전에서 OK struct S98 { static const int x = 0, y = 1; }; // C++11 이전에서 OK void emu() { std::cout << (static_cast<int>(E11::y) + E98::y + N98::y + S98::y) << '\n'; // 4 } namespace cxx20 { enum class long_long_long_name { x, y }; void using_enum_demo() { std::cout << "C++20 `using enum`: __cpp_using_enum == "; switch (auto rnd = []{return long_long_long_name::x;}; rnd()) { #if defined(__cpp_using_enum) using enum long_long_long_name; case x: std::cout << __cpp_using_enum << "; x\n"; break; case y: std::cout << __cpp_using_enum << "; y\n"; break; #else case long_long_long_name::x: std::cout << "?; x\n"; break; case long_long_long_name::y: std::cout << "?; y\n"; break; #endif } } } int main() { color col = red; altitude a; a = altitude::low; std::cout << "col = " << col << '\n' << "a = " << a << '\n' << "f = " << f << '\n'; cxx20::using_enum_demo(); }
가능한 출력:
col = red a = l f = 3 C++20 `using enum`: __cpp_using_enum == 201907; x
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 377 | C++98 |
모든 열거자 값을 표현할 수 있는 정수형이
없을 때의 동작이 명시되지 않음 |
이 경우 열거형은
잘못된 형식 |
| CWG 518 | C++98 | 열거자 목록 뒤에 trailing comma를 허용하지 않음 | 허용됨 |
| CWG 1514 | C++11 |
고정된 기반 타입을 가진 열거형 재정의가
클래스 멤버 선언에서 비트 필드로 파싱될 수 있었음 |
항상 재정의로 파싱됨 |
| CWG 1638 | C++11 |
불투명 열거형 선언의 문법이
템플릿 특수화 사용을 금지함 |
중첩 이름 지정자가
허용됨 |
| CWG 1766 | C++98 |
범위를 벗어난 값을 고정된 기반 타입이 없는
열거형으로 캐스팅할 때 결과가 명시되지 않음 |
동작이 정의되지 않음 |
| CWG 1966 | C++11 |
CWG issue 1514
의 해결책이
:
를 조건식의 일부로 만들어 enum-base 의 일부로 만듦 |
해결책을
멤버 선언 지정자에만 적용 |
| CWG 2156 | C++11 |
열거형 정의가 using 선언을 통해
열거형 타입을 정의할 수 있었음 |
금지됨 |
| CWG 2157 | C++11 |
CWG issue 1966
의 해결책이
한정된 열거형 이름을 포함하지 않음 |
포함됨 |
| CWG 2530 | C++98 |
열거자 목록에 동일한 식별자를 가진
여러 열거자가 포함될 수 있었음 |
금지됨 |
| CWG 2590 | C++98 |
열거형의 크기, 값 표현 및 정렬 요구사항이
기반 타입에 의존하지 않음 |
모두 기반 타입의
요구사항과 동일함 |
| CWG 2621 | C++20 |
using
enum
선언에서 사용된
열거형 이름 조회가 불명확함 |
명확해짐 |
| CWG 2877 | C++20 |
using
enum
선언에서 사용된
열거형 이름 조회가 타입 전용이 아니었음 |
타입 전용으로 변경됨 |
참고문헌
- C++23 표준 (ISO/IEC 14882:2024):
-
- 9.7.1 열거형 선언 [dcl.enum]
- C++20 표준(ISO/IEC 14882:2020):
-
- 9.7.1 열거형 선언 [dcl.enum]
- C++17 표준 (ISO/IEC 14882:2017):
-
- 10.2 열거형 선언 [dcl.enum]
- C++14 표준(ISO/IEC 14882:2014):
-
- 7.2 열거형 선언 [dcl.enum]
- C++11 표준 (ISO/IEC 14882:2011):
-
- 7.2 열거형 선언 [dcl.enum]
- C++03 표준(ISO/IEC 14882:2003):
-
- 7.2 열거형 선언 [dcl.enum]
- C++98 표준 (ISO/IEC 14882:1998):
-
- 7.2 열거형 선언 [dcl.enum]
참고 항목
|
(C++11)
|
타입이 열거형인지 확인합니다
(클래스 템플릿) |
|
(C++23)
|
타입이 범위 지정 열거형인지 확인합니다
(클래스 템플릿) |
|
(C++11)
|
주어진 열거형의 기반 정수 타입을 얻습니다
(클래스 템플릿) |
|
(C++23)
|
열거형을 해당 기반 타입으로 변환합니다
(함수 템플릿) |
|
C 문서
for
열거형
|
|