-
항목10. 범위 있는 enum을 선호하라.Effective Modern C++ 2022. 7. 23. 18:12
- 범위있는 enum의 첫번째 장점 - 열거자들의 이름
범위 없는 enum은 enum이 속한 범위에 열거자들 이름이 속함.
그래서 그 범위에 같은 이름이 있으면 안됨.enum Color { black, white, red }; // Color가 속한 범위에 속함 auto white = false; // 오류! 이 범위에 이미 white가 선언되어 있음.범위 있는 enum( enum 클래스라고도 함)는 enum의 범위에 열거자들이 속함.
enum class Color { black, white, red }; // Color의 범위에 속함 auto white = false; // OK; 이 범위에 다른 "white"는 없음 Color c = white; // 오류! 이 범위에 "white"라는 // 이름의 열거자가 없음 Color c = Color::white; // OK- 또 다른 장점은 범위 있는 enum은 그 열거자들의 형식이 훨씬 강력하게 적용된다는 것이다.
범위없는 enum의 열거자는 암묵적으로 정수 형식으로 변환된다. 하지만 범위있는 enum의 열거자는 암묵적으로 다른 형식으로 변환되지 않는다.
만약 어떤 이유로 범위있는 enum의 열거자를 다른 형식으로 변환하고 싶으면 캐스팅을 사용하면 된다.//위의 코드와 이어짐 if(static_cast<double>(c) < 14.5){ //이상한 코드지만 어쨋든 유효함 ... }- 세번째 장점으로는 전방선언이 가능하다는 것이다.
enum Color; // 오류!전방선언은 컴파일과 관련해 큰 이점을 준다.
enum class Status; // 전방 선언 void continueProcessing(Status s); // 전방 선언된 enum 사용이때 Status의 정의가 바뀌어도, 이 선언들을 담은 헤더파일은 다시 컴파일할 필요가 없다.
더욱이 Status가 수정되고 그것이 continueProcessing의 행동에 영향을 주지 않는다면 continueProcessing의 구현 역시 다시 컴파일할 필요가 없다.전방 선언이 가능하려면 enum이 쓰이기 전에 그 크기를 컴파일러가 알아야한다.
범위 있는 enum의 바탕 형식은 기본적으로 int이다. 그 기본 형식이 마음에 들지 않는다면 다른 형식을 명시적으로 지정하면 된다.enum class Status: std::unit32_t; // 범위 있는 enum의 바탕 형식은 std::unit32_t이 원리를 바탕으로하면 범위 없는 enum에 바탕 형식을 지정해주면 전방 선언이 가능해진다.
하지만 범위 없는 enum이 유효한 상황도 적어도 하나는 존재한다. 바로 C++11의 std::tuple 안에 있는 필드들을 지칭할 때이다.
using UserInfo = std::tuple<std::string, // 사용자 이름 std::string, // 이메일 주소 std::string> ; // 평판치나중에 필드를 이용할 때 어느 필드가 사용자 이름인지 평판치인지를 알 수 없다. 이때 범위 없는 enum을 이용해 필드 이름을 필드 번호에 연관해 사용할 수 있다.
enum UserUnfoFields { uiName, uiEmail, uiReputation }; UserInfo uInfo; ... auto val = std::get<uiEmail>(uInfo); // 이메일 필드의 값을 얻음이 명확함. /* 만약 범위 있는 enum을 사용한다 */ enum class UserUnfoFields { uiName, uiEmail, uiReputation }; UserInfo uInfo; ... auto val = std::get<static_cast<std::size_t>(UserInfoFields::uiEmail)>(uInfo); // 보기만 해도 아찔하다.이것이 가능한 이유는 UserUnfoFields 에서 std::size_t으로 암묵적 변환 덕분이다. (범위 있는 enum은 암묵적 변환을 지원하지 않는다.)
열거자 하나를 받아서 그에 해당하는 std::size_t 값을 돌려주는 함수를 작성해서 사용도 가능하다. constexpr 함수를 이용해 컴파일 도중에 결과를 산출한다.
template<typename E> constexpr typename std::underlying_type<E>::type toUType(E enumerator) noexcept { return static_cast<typename std::underlying_type<E>::type>(enumerator); } /* C++14 */ template<typename E> constexpr auto toUType(E enumerator) noexcept { return static_cast<std::underlying_type_t<E>>(enumerator); }toUtype을 이용하면 튜플의 한 필드에 다음과 같이 접근할 수 있다.
auto val = std::get<toUtype(UserInfoFields::uiEmail)>(uInfO);'Effective Modern C++' 카테고리의 다른 글
항목12. 재정의 함수들을 override로 선언하라 (0) 2022.07.26 항목11. 정의되지 않은 비공개 함수보다 삭제된 함수를 선호하라 (0) 2022.07.25 항목9. typedef보다 별칭 선언을 선호하라 (0) 2022.07.20 항목8. 0과 NULL보다 nullptr을 선호하라 (0) 2022.07.20 항목7. 객체 생성시 () 와 {} 를 구분하라 (0) 2022.07.19