[C++] 범위 있는( scoped ) enum에 관하여

범위 없는( unscoped ) enum

C++에서 보통의 식별자들의 경우, 중괄호 블록을 벗어나게 되면, 효력을 잃게 마련입니다.

{
    int val = 0;
}

val += 10;	// error ! 변수의 범위를 벗어났음

 

그렇지만, 다음의 enum 같은 경우, 이러한 제약에 영향을 받지 않습니다.

enum Color{ white, black, red };

auto white = false; // error !

▼출력

error: 'auto white' redeclared as different kind of symbol

위의 오류는 Color의 white와 bool 변수인 white 이름이 충돌하기 때문에 발생했습니다.

이러한 enum을 범위 없는( unscoped ) enum이라고 부릅니다.

 

이 범위 없는 enum은 심지어, 완전히 분리되었다고 생각되는 다음의 코드에서도 오류가 발생합니다.

enum Color{ white, black, red };
enum Color2{ white, yellow, blue };	// error !! white 식별자 중복

 

그리고, 이 enum의 열거자들은 암시적으로 정수 타입으로 변환됩니다.

그런데, 정수 타입은 필요한 경우 다시 double 타입으로 변환될 수 있으므로, 다음의 코드도 아무런 무리 없이 컴파일됩니다.

int main(){

    enum Color{ white, black, red };

    int result = 0;

    if ( black > 3.5){	// ??
        result = 1;
    }

    return result;
}

 

또한, 범위 없는 enum은 전방 선언( forward declaration )을 할 수 없습니다.

왜냐하면, 컴파일러가 enum의 열거자들을 모두 담을 수 있는 타입을 정할 수 없기 때문입니다.

( 아직 열거자들을 명시하지 않았으므로 )

따라서, 다음과 같은 코드는 오류입니다.

enum Color; // 전방 선언. error !!

void UseEnum( Color e);

 

참고로, 전방 선언이란 식별자를 정의하기 전에, 식별자의 존재를 컴파일러에 미리 알리는 것입니다.

이렇게 함으로써, 컴파일 시간을 대폭 줄일 수 있게 됩니다.

예를 들면, 함수 선언은 헤더 파일에서 하고, 함수는 구현은 다른 파일에 둡니다.( cpp 같은 )

그러면, 이 함수를 사용하는 다른 코드들은, 함수의 구현과 무관하게 함수를 사용할 수 있을 뿐 아니라, 함수의 구현 부분을 수정하더라도, 재컴파일 할 필요가 없게 됩니다.

 

범위 있는( scoped ) enum

위에서 나열한 범위 없는 enum의 문제점들을 해결하기 위해 도입된 것은 바로 범위 있는 enum 구문입니다.

범위 있는 enum은 enum 뒤에 class 키워드를 사용해서 enum 구문을 선언하게 됩니다.

enum class Color1{ white, black, red };  // 범위 있는 enum

enum class Color2{ white, yellow, blue };

 

이 enum에는 범위가 적용되므로, Color1의 white와 Color2의 white는 구분이 됩니다.

그리고, 이러한 구분을 위해서, enum의 열거자에 접근하려면 다음과 같이 해야 합니다.

enum class Color{ white, black, red }; 
Color c = Color::red;	// red 열거자에 접근

 

그리고, 이 enum의 타입은 다른 타입으로의 암시적인 변환을 할 수 없습니다.

따라서, 다음의 코드는 오류입니다.

int main(){

    enum class Color{ white = 100, black, red };  // 범위 있는 enum
        
    int result = 0;

    if ( Color::black > 3.5){   // 암시적인 변환 금지. error !!
        result = 1;
    }

    return result;
}

 

만약, 이 enum 타입의 변환이 필요하다면, 명시적인 변환을 거쳐서 위의 비교 구문을 수행할 수는 있습니다.

int main(){

    enum class Color{ white = 100, black, red };  // 범위 있는 enum
        
    int result = 0;

    if ( static_cast<double>( Color::black ) > 3.5){   // 명시적인 변환
        result = 1;
    }

    return result;
}

 

명시적인 타입 변환 방식인 static_cast에 관한 글은 여기에서 볼 수 있습니다.

 

[C++] 명시적인 C-style cast와 static_cast의 차이점

암시적인 형 변환과 명시적인 형 변환암시적인 형 변환( type conversion )은 변수의 타입이 일치하지 않는 경우, 컴파일러가 자동으로 필요한 타입으로 변경하는 것을 말합니다. 아래의 예제가 암

codingembers.tistory.com

 

그리고, 범위 있는 enum은 전방 선언이 가능합니다.

이것은 이 enum의, 바탕 타입( underlying type )이라는 하는, 기본 타입을 int로 컴파일러에게 알려주기 때문입니다.

그래서, 범위 없는 enum과 달리 다음 코드가 가능합니다.

enum class Color; // 전방 선언. ok

void UseEnum( Color e);

 

게다가, 원하는 경우 enum의 바탕 타입을 변경할 수도 있습니다.

enum class Color: std::uint32_t; // 바탕 타입 변경

enum Unscoped_Color: uint8_t;   // 범위 없는 enum의 바탕 타입

그리고, 범위 있는 enum 도입과 동시에, 기존의 enum도 바탕 타입을 설정할 수 있게 되었습니다.

기존의 범위 없는 enum의 전방 선언이 불가능했던 것은 열거자의 타입을 설정할 수 없었기 때문이므로, 이젠 범위 없는 enum도 위와 같이 방식으로 전방 선언이 가능하게 되었습니다.

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유