enum class를 자유롭게 사용해보자

1 분 소요

enum class

enum은 원리 타입을 엄격하게 따지지 않는다. 참고로 타입을 엄격히 따지는 것을 스트롱 타입 (strong type) 이라 하고 타입에 안전하다고 표현한다. enum 타입은 항상 정수로 해석하기 때문에 선언한 형태에 관계없이 모든 enum 타입을 서로 비교할 수 없다.
타입을 엄격하게 적용하고 싶다면 enum class를 사용한다. 다음과 같이 정의할 수 있다.
enum class PieceType
{
    King = 1;
    Queen,
    Rook = 10,
    Pawn
};
enum class로 정의한 열거 타입 값들의 이름은 스코프(유효 범위)가 자동으로 확장되지 않는다. 다시 말해 enum class 스코프 안에서만 유효하다. 따라서 열거 타입 값을 사용할 때마다 다음과 같이 스코프 지정 연산자를 붙여야 한다.
PieceType piece = PieceType::King;
만약 if문 안에서 일반 정수형태의 값과 비교하기 위해서는 명시적으로 형변환해야 한다. 즉
if(PieceType::Quene == 2) { ... }
과 같이 표현할 수 없고, 다음과 같이 표현해야 한다.
if(static_cast<int>(PieceType::Quene) == 2) { ... }

enum class도 메서드를 가질 수 있는가?

enum class는 메서드를 가질 수 없다. 하지만 일반 class를 이용하여 기능을 구현 할 수 있다. 아래 예를 확인해보자
#define SWITCHEN

class Mode
{
  public:
    enum Value : uint8_t
    {
        sleep,
        idle,
        overrun,
        error
    };

    Mode() = default;
    constexpr Mode(Value value) : _value(value) {}

#ifdef SWITCHEN // Enable switch(Mode)
    constexpr operator Value() const
    {
        return _value;
    }
    explicit operator bool() const = delete;

#else
    constexpr bool operator==(Mode mode) const
    {
        return _value == mode._value;
    }
    constexpr bool operator!=(Mode mode) const
    {
        return _value != mode._value;
    }
#endif

    constexpr bool IsError() const
    {
        return _value == Value::error;
    }

  private:
    Value _value;
};

int main(int argc, char *argv[])
{
    Mode mode = Mode::idle;

    switch (mode)
    {
    case Mode::error:
        break;
    case Mode::sleep:
        break;
    case Mode::idle:
        break;
    case Mode::overrun:
        break;
    }

    return -1;
}
위 예에서 컴파일러는 다음과 같은 코드를 걸러낸다.
Mode mode = 1;
그리고 쉽게 메서드를 추가할 수 있다.
Mode mode("sleep");
mode.toString();

etc.

explicit 키워드는 자신이 원하지 않는 형변환이 일어나지 않도록 제한하는 키워드이다.
constexpr 키워드는 컴파일 시간 상수를 만든다. 컴파일 시간에 결정되는 상수 값으로만 초기화 할 수 있다.
  • const와 constexpr의 주요 차이점은 const 변수의 초기화를 런타임까지 지연시킬 수 있는 반면, constexpr 변수는 반드시 컴파일 타임에 초기화가 되어 있어야 한다.
  • 초기화가 안 되었거나, 상수가 아닌 값으로 초기화 시도시 컴파일이 되지 않는다.
  • 함수에서 사용할 때는 inline을 암시한다. 즉, 컴파일 타임에 평가하기 때문이며, inline함수들과 같이 컴파일된다.

참고