Enum class

###DESCRIPTION_HERE###


enum class

enum class는 기존 enum의 문제점들을 보완하기 위해 등장하였다.

scope

enum에 선언된 내용은 enum과 같은 scope 범위를 갖는다.

1
2
3
enum Color { black, white, red };
...
int red = 0; // error

enum class에 선언된 내용은 그 내부로 scope가 제한된다.

1
2
3
4
5
enum class Color { black, white, red };
...
int red = 0; // OK

Color c = Color::black;

implicit conversion

enum은 암묵적으로 정수 타입으로 변환된다.

1
2
3
4
5
6
enum Color { black, white, red };
Color c = white;
...

if (c < 14.5) // Color와 숫자를 비교
... // 암묵적 형변환으로 컴파일에 문제가 없음

enum class는 암묵적으로 형변환되지 않기 때문에 위와같은 실수를 막을 수 있다.

1
2
3
4
5
6
enum class Color { black, white, red };
Color c = Color::white;
...

if (c < 14.5) // 컴파일 에러.
... // Color와 double을 비교할 수 없음

만약 형변환이 필요하다면 static_cast와 같은 명시적 형 변환을 해야한다.


묵시적 형변환을 제한하는 enum class는 비트연산에서 사용하기에 상당히 까다롭다.

1
2
3
4
using UserInfo = 
std::tuple<std::string, // 사용자 이름
std::string, // 이메일 주소
std::size_t>; // 평판

UserInfo에서 이메일 주소를 가져오는 방법은 아래와 같다.

1
2
3
4
UserInfo info;
...

auto val = std::get<1>(info);

사용자입장에서 UserInfo의 비트필드 1에 어떤 정보가 있는지 헷갈릴 수 있다.
enum을 이용해서 이를 좀더 명확히 할 수 있다.

1
2
3
4
5
enum UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo info;
...

auto val = std::get<uiEmail>(info);

이런 연산이 가능한 이유는 enum은 명시적 형변환을 지원하기 때문이다.


만약 enum class를 사용한다면 아래처럼 코드가 복잡해질 것이다.

1
2
3
4
5
6
7
enum class UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo info;
...

auto val =
std::get<static_cast<std::size_t>(UserInfoFields::uiEmail)>
(info);

enum class를 받아서 std::size_t를 반환하는 함수를 작성하면 문제가 나아질 수 있다.
먼저 std::get은 템플릿이다. 몇 번째 비트 필드를 가져올지는 템플릿 인자로 전달된다. 따라서 우리가 작성하는 함수는 컴파일 타임에 결과를 반환하는 constexpr로 작성해야 할것이다.

또 범용성을 위해 모든 enum class타입을 받도록 템플릿 함수가 되어야 한다. 그리고 그 반환 타입 역시 std::size_t대신 해당 enum classunderlying_type이 되는것이 바람직 하다. 마지막으로 이 함수는 예외를 던지지 않을 것이므로 noexcept로 선언되어야 한다.

C++11
1
2
3
4
5
6
template <typename E>
constexpr typename std::underlying_type<E>::type
toUType(E enumerator) noexcept
{
return static_cast<std::underlying_type_t<E>>(enumerator);
}

C++14의 alias declaration을 사용하면 반환타입을 간소화 할 수 있다.

C++14
1
2
3
4
5
6
template <typename E>
constexpr std::underlying_type_t<E>
toUType(E enumerator) noexcept
{
return static_cast<std::underlying_type_t<E>>(enumerator);
}

C++14가 지원하는 auto반환 타입을 사용할 수도 있다.

C++14
1
2
3
4
5
6
template <typename E>
constexpr auto
toUType(E enumerator) noexcept
{
return static_cast<std::underlying_type_t<E>>(enumerator);
}

작성한 함수를 사용하면 튜플의 비트 필드에 다음과 같이 접근할 수 있다.

1
auto val = std::get<toUType(UserInfoFields::uiEmail)>(info);


References

Author

Joyus.Gim

Posted on

2022-07-26

Updated on

2022-07-26

Licensed under

Comments