UserInfo uInfo; // tuple 형식의 객체
...
auto val = std::get<1>(uInfo); // 필드 1의 값을 얻는다
안 그래도 프로그래머는 많은 것을 머리에 담고 있어야 하는데, 과연 사용자가 필드 1이 이메일 주소라는 점까지 기억하고 있을까요?? 아마 그렇지는 않을 것입니다. 범위 없는 enum으로 필드 번호를 필드 이름에 연관시키면 그런 것들을 일일이 기억할 필요가 없습니다.
enum UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo uInfo; // 이전과 동일
...
auto val = std::get(uInfo); // 이메일 필드의 값을 얻음이 명확함
이 코드가 작동하는 것은 UserInfoFields에서 std::size_t(std::get이 요구하는 형식)로의 암묵적 변환 덕분입니다.
이에 해당하는 범위 있는 enum 버전은 훨씬 장황합니다.
enum class UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo uInfo; // 이전과 동일
...
auto val = std::get(UserInfoFields::uiEmail)>(uInfo);
열거자 하나를 받아서 그에 해당하는 std::size_t 값을 돌려주는 함수를 작성한다면 장황함이 줄어들겠지만, 그런 함수를 작성하는 것이 다소 까다롭습니다. std::get은 하나의 템플릿이며, 여기에 넘겨주는 것은 함수 인수가 아니라 템플릿 인수입니다(괄호가 아니라 꺽쇠를 사용했음을 주목할 것). 따라서 열거자를 std::size_t로 변환하는 함수는 그 결과를 컴파일 도중에 산출해야 합니다. Chapter(15)에서 설명하겠지만, 그러려면 그 함수는 반드시 constexpr 함수이어야 합니다.
좀 더 정확하게 말하면 그러한 함수는 constexpr 함수 템플릿이어야 합니다. 그 어떤 종류의 enum에 대해서도 작동해야 하기 때문입니다. 즉, std::size_t를 돌려주는 것이 아니라 enum의 바탕 형식을 돌려주어야 합니다. 이 부분은 std::underlying_type 형식 특질을 사용하면 됩니다. (형식 특질에 관해서는 Chapter(9)를 보세요.) 마지막으로, 그 함수는 noexcept(Chapter(14)를 보시오)로 선언해야 합니다. 결코 예외를 던지지 않을 것을 알고 있기 때문입니다. 다음은 이상의 사항들을 모두 고려해서 만든, 임의의 열거자 하나를 받아서 그 값을 컴파일 시점 상수로서 돌려주는 함수 템플릿 toUType입니다.
template
constexpr typename std::underlying_type::type
toType(E enumerator) noexcept
{
return
static_cast::type>(enumerator);
}
C++14에서는 typename std::underlying_type::type을 깔끔한 std::underlying_type_t(Chapter(9) 참고)로 대체해서 toUType을 더욱 단순화할 수 있습니다.
template // C++14
constexpr std::underlying_type_t
toUType(E enumerator) noexcept
{
return static_cast>(enumerator);
}
C++14가 지원하는 auto 반환 형식(Chapter(3) 참고)을 적용하면 더욱 깔끔해집니다.
template // C++14
constexpr auto
toUType(E enumerator) noexcept
{
return static_cast>(enumerator);
}
어떤 형태로 구현했든, toUType을 이용하면 튜플의 한 필드에 다음과 같이 접근할 수 있습니다.
auto val = std:;get(uInfo);
범위 없는 enum을 사용할 때보다는 여전히 타자량이 많지만, 그래도 이름 공간 오염을 피하고 열거자들과 관련된 의도치 않은 변환이 방지된다는 장점이 있습니다. 글자 몇 개를 더 타자해야 한다고 해도, 2400보(baud) 모뎀이 최첨단 디지털 통신 장비였던 시절에 만들어진 열거형 기술의 함정들을 피하는 것이 더 이득인 경우가 많습니다.
*** Key Point ***
- C++98 스타일의 enum을 이제는 범위 없는 enum이라고 부릅니다.
- 범위 있는 enum의 열거자들은 그 안에서만 보입니다. 이 열거자들은 오직 캐스팅을 통해서만 다른 형식으로 변환됩니다.
- 범위 있는 enum과 범위 없는 enum 모두 바탕 형식 지정을 지원합니다. 범위 있는 enum의 기본 바탕 형식은 int입니다. 범위 없는 enum에는 기본 바탕 형식이 없습니다.
- 범위 있는 enum은 항상 전방 선언이 가능합니다. 범위 없는 enum은 해당 선언에 바탕 형식을 지정하는 경우에만 전방 선언이 가능합니다.
'컴퓨터과학' 카테고리의 다른 글
[Effective C++] (11-2) 정의되지 않은 비공개 함수보다 삭제된 함수를 선호할 것 (0) | 2020.05.28 |
---|---|
[Effective C++] (11-1) 정의되지 않은 비공개 함수보다 삭제된 함수를 선호할 것 (0) | 2020.05.28 |
[Effective C++] (10-2) 범위 없는 enum보다 범위 있는 enum을 선호할 것 (0) | 2020.05.28 |
[Effective C++] (10-1) 범위 없는 enum보다 범위 있는 enum을 선호할 것 (0) | 2020.05.28 |
[Effective C++] (9-2) typedef보다 별칭 선언을 선호할 것 (0) | 2020.05.28 |