다음을 통해 공유


열거형(C++)

열거형은 열거자라는 명명된 정수 상수 집합으로 구성된 사용자 정의 형식입니다.

참고 항목

이 문서에서는 ISO 표준 C++ 언어 enum 형식과 C++11에 도입된 범위가 지정된(또는 강력한 형식의) enum class 형식을 다룹니다. C++/CLI 및 C++/CX의 public enum class 또는 private enum class 형식에 대한 자세한 내용은 enum class(C++/CLI 및 C++/CX)를 참조하세요.

구문

:
identifier

:
enum-head { enumerator-listopt }
enum-head { enumerator-list , }

:
enum-key attribute-specifier-seqopt enum-head-nameopt enum-baseopt

:
nested-name-specifieropt identifier

:
enum-key attribute-specifier-seq optenum-head-name enum-baseopt ;

:
enum
enum class
enum struct

:
: type-specifier-seq

:
enumerator-definition
enumerator-list , enumerator-definition

:
enumerator
enumerator = constant-expression

:
identifier attribute-specifier-seqopt

사용

// unscoped enum:
// enum [identifier] [: type] {enum-list};

// scoped enum:
// enum [class|struct] [identifier] [: type] {enum-list};

// Forward declaration of enumerations  (C++11):
enum A : int;          // non-scoped enum must have type specified
enum class B;          // scoped enum defaults to int but ...
enum class C : short;  // ... may have any integral underlying type

매개 변수

identifier
열거형에 지정된 형식 이름입니다.

type
열거자의 기본 형식이며, 모든 열거자는 동일한 기본 형식을 갖습니다. 모든 정수 계열 형식이 가능합니다.

enum-list
열거형에서 열거자의 쉼표로 구분된 목록입니다. 범위에 있는 모든 열거자 또는 변수 이름은 고유해야 합니다. 하지만 값이 중복될 수 있습니다. 범위가 정해지지 않은 열거형에서 범위는 주변 범위이며 범위가 지정된 열거형에서 범위는 enum-list 자체입니다. 범위가 지정된 열거형에서는 목록이 비어 있을 수 있으며 이는 사실상 새 정수 형식을 정의합니다.

class
이 키워드를 선언에 사용하여, 열거형의 범위가 지정되고 identifier가 제공되도록 지정합니다. struct 키워드를 class 대신 사용할 수도 있습니다. 이 컨텍스트에서는 이러한 키워드의 의미 체계가 같기 때문입니다.

열거자 범위

열거형은 명명된 상수로 표시되는 값 범위를 설명하는 컨텍스트를 제공합니다. 이러한 명명된 상수를 열거자라고도 합니다. 원래 C 및 C++ enum 형식에서는 enum이 선언된 범위 전체에서 한정되지 않은 열거자가 표시됩니다. 범위가 지정된 열거형에서 열거자 이름은 enum 형식 이름으로 한정되어야 합니다. 다음 예제에서는 두 가지 열거형의 이러한 기본적인 차이점을 보여 줍니다.

namespace CardGame_Scoped
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Suit::Clubs) // Enumerator must be qualified by enum type
        { /*...*/}
    }
}

namespace CardGame_NonScoped
{
    enum Suit { Diamonds, Hearts, Clubs, Spades };

    void PlayCard(Suit suit)
    {
        if (suit == Clubs) // Enumerator is visible without qualification
        { /*...*/
        }
    }
}

열거형의 각 이름은 열거형 값의 순서대로 해당 위치에 해당하는 정수 값이 할당됩니다. 기본적으로 첫 번째 값은 0이 할당되고 다음 값은 1이 할당되는 식이지만 여기에 나와 있는 대로 열거자의 값을 명시적으로 설정할 수 있습니다.

enum Suit { Diamonds = 1, Hearts, Clubs, Spades };

Diamonds 열거자에 값 1이 할당됩니다. 명시적인 값이 지정되지 않은 경우 다음 열거자는 이전 열거자에 1을 더한 값을 받습니다. 앞의 예제에서 Hearts의 값은 2, Clubs의 값은 3 등이 될 수 있습니다.

모든 열거자는 상수로 처리되며 enum이 정의되는 범위 내에(범위가 지정되지 않은 열거형의 경우) 또는 enum 자체에(범위가 지정된 열거형의 경우) 고유한 이름이 있어야 합니다. 이름에 지정된 값은 고유할 필요가 없습니다. 예를 들어, 범위가 지정되지 않은 열거형 Suit의 다음 선언을 고려해보세요.

enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };

Diamonds, Hearts, Clubs, Spades의 값은 각각 5, 6, 4, 5입니다. 5가 한 번 이상 사용되며 이는 의도한 것이 아니더라도 허용됩니다. 이러한 규칙은 범위가 지정된 열거형의 경우와 동일합니다.

캐스팅 규칙

범위가 지정되지 않은 열거형 상수를 int로 암시적으로 변환할 수 있지만, int는 열거형 값으로 암시적으로 변환할 수 없습니다. 다음 예에서는 handSuit가 아닌 값을 할당하면 어떻게 되는지를 보여 줍니다.

int account_num = 135692;
Suit hand;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'

int를 범위가 지정된 열거자 또는 범위가 지정되지 않은 열거자로 변환하려면 캐스팅해야 합니다. 캐스팅하지 않고 범위가 지정된 열거자를 정수 값으로 승격할 수 있습니다.

int account_num = Hearts; //OK if Hearts is in an unscoped enum

이러한 방식으로 암시적 변환을 사용할 경우 의도하지 않은 문제가 발생할 수 있습니다. 범위가 지정되지 않은 열거형과 관련된 프로그래밍 오류를 제거하기 위해 범위가 지정된 열거형 값은 강력한 형식입니다. 다음 예제에 표시된 것처럼 범위가 지정된 열거자는 열거형 형식 이름(식별자)으로 한정되어야 하며 암시적으로 변환할 수 없습니다.

namespace ScopedEnumConversions
{
    enum class Suit { Diamonds, Hearts, Clubs, Spades };

    void AttemptConversions()
    {
        Suit hand;
        hand = Clubs; // error C2065: 'Clubs' : undeclared identifier
        hand = Suit::Clubs; //Correct.
        int account_num = 135692;
        hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
        hand = static_cast<Suit>(account_num); // OK, but probably a bug!!!

        account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int'
        account_num = static_cast<int>(Suit::Hearts); // OK
    }
}

hand = account_num;은 앞에서 보았듯이 범위가 지정되지 않은 열거형에 발생하는 오류를 초래합니다. 명시적 캐스트를 사용하면 허용됩니다. 그러나 범위가 지정된 열거형을 사용하여 다음 문 account_num = Suit::Hearts;에서 시도된 변환은 명시적 캐스트 없이는 더 이상 허용되지 않습니다.

열거자가 없는 열거형

Visual Studio 2017 버전 15.3 이상(/std:c++17 이상에서 사용 가능): 열거자 없이 명시적 기본 형식을 사용하여 열거형(일반 또는 범위가 지정된)을 정의하면 사실상 다른 형식으로의 암시적 변환이 없는 새로운 정수 형식을 도입할 수 있습니다. 기본 제공 기본 형식 대신 이 형식을 사용하면 의도치 않은 암시적 변환으로 인해 발생할 수 있는 미묘한 오류를 제거할 수 있습니다.

enum class byte : unsigned char { };

새 형식은 기본 형식의 정확한 복사본이므로 동일한 호출 규칙을 갖습니다. 즉, 성능 저하 없이 ABI 전체에서 사용할 수 있습니다. 직접 목록 초기화를 사용하여 형식의 변수를 초기화하는 경우 캐스트가 필요하지 않습니다. 다음 예에서는 다양한 컨텍스트에서 열거자 없이 열거형을 초기화하는 방법을 보여 줍니다.

enum class byte : unsigned char { };

enum class E : int { };
E e1{ 0 };
E e2 = E{ 0 };

struct X
{
    E e{ 0 };
    X() : e{ 0 } { }
};

E* p = new E{ 0 };

void f(E e) {};

int main()
{
    f(E{ 0 });
    byte i{ 42 };
    byte j = byte{ 42 };

    // unsigned char c = j; // C2440: 'initializing': cannot convert from 'byte' to 'unsigned char'
    return 0;
}

참고 항목

C 열거형 선언
키워드