[C++23 새기능 소개] std::to_underlying

C++11부터 열거형(enum)은 enum class를 통해 타입 안전한 열거형을 지원하면서 강타입 열거형(strongly typed enum)을 도입했습니다. 하지만 여전히 열거형 값을 기반 정수 타입으로 변환할 때는 캐스팅(static_cast 등)을 수동으로 해야 했습니다. C++23에서는 이러한 불편함을 줄이기 위해 std::to_underlying 함수를 도입하였습니다. 이를 통해 강타입 열거형 값을 기저 타입(underlying type)으로 쉽게 변환할 수 있습니다.

 

이번 글에서는 std::to_underlying의 개념과 사용법, 그리고 이전 버전과 비교하여 어떠한 개선점을 제공하는지 알아보겠습니다.

std::to_underlying란 무엇인가요?

std::to_underlying 함수는 C++23에서 추가된 정적 함수 템플릿으로, 강타입 열거형 값을 그 기저 정수 타입으로 변환한 값을 반환합니다. 즉, enum class Color { Red=1, Green=2, Blue=3 };에서 Color::Green을 std::to_underlying(Color::Green)로 호출하면 해당 열거형의 기저 타입(예: int)의 값인 2를 반환합니다.

이전 버전에서는 어떻게 했나요?

C++11 이후 강타입 열거형을 도입하면서, 열거형 값을 정수 타입으로 변환하기 위해서는 다음과 같이 명시적 캐스팅을 해야 했습니다.

예제: 기존 방식(C++20까지)

enum class Color : int { Red = 1, Green = 2, Blue = 3 };

int main() {
    Color c = Color::Green;

    // 기저 타입으로 변환: 명시적 캐스팅 필요
    int value = static_cast<int>(c);

    // value는 2
    return 0;
}
  • 문제점: 열거형을 기저 타입으로 변환할 때마다 매번 static_cast<int>와 같은 캐스팅을 해야 했고, 이는 코드 가독성과 유지보수성을 떨어뜨렸습니다. 또한, 기저 타입이 int가 아닐 수도 있으므로 캐스팅 타입을 일일이 맞추어야 하는 번거로움이 있었습니다.

C++23의 std::to_underlying 사용 예제

#include <utility> // std::to_underlying
#include <iostream>

enum class Color : unsigned char { Red = 1, Green = 2, Blue = 3 };

int main() {
    Color c = Color::Green;

    // std::to_underlying를 사용하여 기저 타입 반환
    auto value = std::to_underlying(c);

    // value의 타입은 Color의 기저 타입(unsigned char)
    // value = 2
    std::cout << static_cast<int>(value) << '\n'; // 출력: 2

    return 0;
}
  • std::to_underlying(c)를 호출하면 열거형 c의 기저 타입 값(여기서는 unsigned char 타입의 값)을 반환합니다.
  • 이제 명시적인 static_cast<int> 같은 변환 없이도 열거형 값을 손쉽게 정수로 변환할 수 있습니다.

어떻게 좋아졌나요?

  • 가독성 향상: std::to_underlying를 사용하면 열거형 값을 정수로 변환하는 의도가 명확하게 드러나 코드 이해가 쉬워집니다.
  • 타입 안전성 개선: std::to_underlying는 열거형에 지정된 기저 타입을 반환하므로, static_cast<int>와 같이 기저 타입을 임의로 가정하는 위험이 줄어듭니다.
  • 유지보수성 향상: 열거형의 기저 타입을 변경하더라도 std::to_underlying 호출부 코드는 변경할 필요가 없으므로 확장성 향상.

상세한 예제와 비교

기저 타입이 다른 열거형

enum class Status : short { OK = 0, ERROR = -1 };

Status s = Status::ERROR;
auto val = std::to_underlying(s);
// val은 short 타입의 -1

// 만약 이전 방식이면 static_cast<short>(s)라고 명시해야 했지만
// to_underlying으로 명확한 변환 가능
  • std::to_underlying는 기저 타입을 알아서 반환하므로 기저 타입 변경에도 안전.

다른 범위와의 조합

#include <ranges>
#include <iostream>

enum class Fruit : unsigned int { Apple = 10, Banana = 20, Cherry = 30 };

int main() {
    Fruit fruits[] = {Fruit::Apple, Fruit::Banana, Fruit::Cherry};

    for (auto val : fruits | std::views::transform(std::to_underlying)) {
        std::cout << val << ' '; // 출력: 10 20 30
    }

    return 0;
}
  • 범위 어댑터와 결합해 열거형 값을 기저 타입으로 일괄 변환하는 로직 구현도 간단.

주의 사항

  • 목적 명확화: std::to_underlying는 단순히 기저 타입 변환만 하므로, 열거형 값의 의미 있는 처리나 검증은 별도로 해야 합니다.
  • C++23 지원 여부: C++23 기능이므로, 지원하는 컴파일러와 표준 라이브러리가 필요합니다.

요약

C++23의 std::to_underlying는 강타입 열거형을 기저 타입으로 변환하는 것을 간결하고 직관적으로 만들어줍니다. 이전에는 static_cast를 통해 명시적 변환이 필요했지만, 이제는 std::to_underlying로 기저 타입 변환을 한 번에 처리할 수 있어 코드 가독성과 유지보수성이 향상됩니다. 이는 열거형을 활용하는 로직에서 특히 유용하며, 열거형 기반 상태 머신, 값 변환 로직 등에 편의를 제공합니다.

 

 

참고 자료:

반응형