Namespaces
Variants

Enumeration declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

열거형 은 값이 특정 범위로 제한되는(자세한 내용은 아래 참조) 고유한 타입으로, 여러 개의 명시적으로 이름이 지정된 상수(" 열거자 ")를 포함할 수 있습니다.

상수들의 값은 열거형의 기반 타입 으로 알려진 정수형 타입의 값입니다. 열거형은 동일한 크기 , 값 표현 , 그리고 정렬 요구사항 을 기반 타입과 공유합니다. 또한, 열거형의 각 값은 기반 타입의 해당 값과 동일한 표현을 가집니다.

열거형은 다음 구문을 사용하여 (재)선언됩니다:

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 이후)
1) enum-specifier , 선언 구문의 decl-specifier-seq 에 나타나는: 열거형 타입과 그 열거자들을 정의합니다.
2) 열거자 목록 뒤에 후행 쉼표를 사용할 수 있습니다.
3) 불투명 열거형 선언: 열거형 타입은 정의하지만 열거자를 정의하지 않음: 이 선언 이후에는 해당 타입이 완전한 타입이 되며 크기가 알려짐
enum-key -

enum

(C++11 이전)

다음 중 하나: enum , enum class , 또는 enum struct

(C++11 이후)
attr - (C++11 이후) 임의 개수의 속성 의 선택적 시퀀스
enum-head-name -

선언되는 열거형의 이름, 생략 가능

(C++11 이전)

선언되는 열거형의 이름, 선택적으로 nested-name-specifier (이름과 범위 확인 연산자 :: 의 시퀀스로, 범위 확인 연산자로 끝남)가 앞에 올 수 있음. 비범위 비투명 열거형 선언에서만 생략 가능.
nested-name-specifier 는 열거형 이름이 존재하고 이 선언이 재선언인 경우에만 나타날 수 있음. 투명 열거형 선언의 경우, nested-name-specifier 명시적 특수화 선언 에서만 열거형 이름 앞에 나타날 수 있음.
nested-name-specifier 가 존재하는 경우, enum-specifier 는 단순히 상속되었거나 using 선언 에 의해 도입된 열거형을 참조할 수 없으며, enum-specifier 는 이전 선언을 포함하는 네임스페이스에서만 나타날 수 있음. 이러한 경우 nested-name-specifier decltype 지정자로 시작할 수 없음.

(C++11 이후)
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 이후)
1) 고정되지 않은 기반 타입을 가진 비범위 열거형을 선언합니다(이 경우 기반 타입은 모든 열거자 값을 표현할 수 있는 구현 정의 정수 타입입니다; 이 타입은 열거자 값이 int 또는 unsigned int 에 맞지 않는 경우가 아니면 int 보다 크지 않습니다. enumerator-list 가 비어 있는 경우, 기반 타입은 값이 0 인 단일 열거자를 가진 열거형인 것처럼 처리됩니다. 모든 열거자 값을 표현할 수 있는 정수 타입이 없는 경우, 열거형은 잘못된 형식입니다).
2) 고정된 기반 타입을 가진 비범위 열거형 타입을 선언합니다.
3) 범위가 없는 열거형에 대한 불투명 열거형 선언은 이름과 기본 타입을 명시해야 합니다.

enumerator 는 열거형의 타입(즉, name )을 가지는 명명된 상수가 되어, 둘러싸는 스코프에서 보이며 상수가 필요한 모든 경우에 사용할 수 있습니다.

enum Color { red, green, blue };
Color r = red;
switch(r)
{
    case red  : std::cout << "빨강\n";   break;
    case green: std::cout << "초록\n"; break;
    case blue : std::cout << "파랑\n";  break;
}

각 열거자는 기본 타입의 값과 연관됩니다. = 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;

선언 지정자 에서 멤버 선언 의 경우, 다음 시퀀스는

enum enum-head-name :

항상 열거형 선언의 일부로 파싱됩니다:

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 이름 을 가지지 않으며, 링크 목적 으로는 그 기반 타입과 첫 번째 열거자로 표시됩니다; 이러한 열거형은 링크 목적의 이름 으로서 열거자를 가진다고 합니다.

범위가 지정된 열거형

enum struct|class name { enumerator = constant-expression , enumerator = constant-expression , ... } (1)
enum struct|class name : type { enumerator = constant-expression , enumerator = constant-expression , ... } (2)
enum struct|class name ; (3)
enum struct|class name : type ; (4)
1) 범위가 지정된 열거형 타입을 선언하며, 기본 타입은 int 입니다 ( class struct 키워드는 완전히 동일합니다)
2) 범위가 지정된 열거형 타입을 선언하며, 기본 타입은 type 입니다
3) 기본 타입이 int 인 범위가 지정된 열거형에 대한 불투명 선언입니다
4) 기본 타입이 type 인 범위가 지정된 열거형에 대한 불투명 선언입니다

enumerator 는 열거형 타입(즉, name )의 명명된 상수가 되며, 열거형의 범위 내에 포함되고 범위 확인 연산자를 사용하여 접근할 수 있습니다. 범위가 지정된 열거자 값에서 정수 타입으로의 암시적 변환은 없지만, static_cast 를 사용하여 열거자의 숫자 값을 얻을 수 있습니다.

#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부터)

열거형은 다음과 같은 모든 조건을 만족할 때 캐스트 없이 정수로부터 초기화될 수 있으며, 목록 초기화 를 사용합니다:

  • 초기화가 직접-목록-초기화(direct-list-initialization)인 경우
  • 초기화자 목록에 단일 요소만 있는 경우
  • 열거형이 범위 있는(scoped) 열거형이거나 기반 타입이 고정된 범위 없는(unscoped) 열거형인 경우
  • 변환이 축소 변환(narrowing)이 아닌 경우

이는 새로운 정수 타입(예: SafeInt )을 도입할 수 있게 하며, 이러한 타입은 ABI에서 구조체를 값으로 전달/반환할 때 불이익이 있는 경우에도 기반 정수 타입과 동일한 기존 호출 규약을 사용할 수 있습니다.

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 선언

using enum using-enum-declarator ; (C++20부터)
declarator - (한정될 수 있는) 식별자 또는 단순 템플릿 식별자


declarator 는 비의존적(non-dependent) 열거형 타입을 지정해야 합니다. 열거형 선언들은 declarator 의 한정 여부에 따라 타입만을 사용한 일반 한정 또는 비한정 이름 검색을 통해 찾습니다.

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

키워드

enum , struct , class , using

예제

#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 문서 for 열거형