본문 바로가기
개발/C++ (98,03,11,14,17,20,23)

Modern C++ : Type Traits 2 (11, 14, 17, 20)

by snowoods 2025. 9. 27.

Modern C++

Type Traits 2

개요

타입 특성(Type Traits)은 컴파일 시간에 타입에 대한 정보를 얻거나 타입을 변환하는 데 사용되는 템플릿 기반의 메커니즘입니다. <type_traits> 헤더에 정의된 클래스 템플릿들을 통해, 특정 타입이 정수형인지, 포인터인지, 혹은 두 타입이 동일한지 등을 확인할 수 있습니다. 이를 활용하면 템플릿 프로그래밍에서 타입에 따른 조건부 컴파일, static_assert를 이용한 타입 제약, 오버로드 해석 제어(SFINAE) 등을 효과적으로 구현할 수 있습니다.

C++ 버전별 주요 키워드 도입 시기

  • C++11: <type_traits> 헤더와 함께 대부분의 핵심 타입 특성들이 도입되었습니다. (std::is_same, std::is_integral, std::enable_if, std::conditional 등)
  • C++14: 타입 특성의 ::type 멤버에 대한 별칭(alias)으로 _t 접미사가 붙은 템플릿들이 추가되었습니다. (예: std::enable_if_t)
  • C++17: 타입 특성의 ::value 멤버에 대한 별칭으로 _v 접미사가 붙은 변수 템플릿들이 추가되었습니다. (예: std::is_same_v)
  • C++20: std::remove_cvref와 같이 참조 및 const/volatile 한정자를 한 번에 제거하는 등 편의성을 높인 타입 특성들이 추가되었습니다.

내용 설명

타입 특성은 주로 다음과 같은 형태로 정보를 제공합니다.

  1. 타입 질의(Type Queries): std::is_integral<T>::value와 같이 bool 타입의 static 멤버 value를 통해 타입의 속성을 확인합니다. C++17부터는 std::is_integral_v<T>처럼 _v 템플릿을 사용하는 것이 더 간결합니다.
  2. 타입 변환(Type Transformations): std::remove_reference<T>::type과 같이 type이라는 static 멤버 타입을 통해 기존 타입을 새로운 타입으로 변환합니다. C++14부터는 std::remove_reference_t<T>처럼 _t 템플릿을 사용하는 것이 편리합니다.

예제 코드는 is_string이라는 커스텀 타입 특성을 정의하여 to_upper_caseto_lower_case 함수에 전달되는 템플릿 인자가 각각 std::stringstd::string_view인지 컴파일 시간에 검증합니다. std::conjunction은 여러 타입 특성 조건을 논리 AND 연산으로 묶어주는 유틸리티로, 모든 조건이 참일 때 true가 됩니다.

static_assert는 컴파일 시간에 주어진 조건이 거짓일 경우 컴파일을 중단시키고 에러 메시지를 출력하는 기능으로, 타입 특성과 함께 사용하면 템플릿의 타입 제약을 명확하게 강제할 수 있습니다.

예제 코드

#include <algorithm>
#include <iostream>
#include <string>
#include <string_view>
#include <type_traits>

template <typename StringType, typename ViewType>
struct is_string
    : public std::conjunction<std::is_same<std::string, StringType>,
                              std::is_same<std::string_view, ViewType>>
{
};

template <typename StringType, typename ViewType>
StringType to_upper_case(ViewType text);

template <typename StringType, typename ViewType>
StringType to_lower_case(ViewType text);

int main()
{
    auto input_text = std::string{};
    std::cout << "Please enter any text: ";
    std::cin >> input_text;

    std::cout << "to_upper_case: "
              << to_upper_case<std::string, std::string_view>(input_text)
              << '\n';
    std::cout << "to_lower_case: "
              << to_lower_case<std::string, std::string_view>(input_text)
              << '\n';

    return 0;
}

template <typename StringType, typename ViewType>
StringType to_upper_case(ViewType text)
{
    static_assert(is_string<StringType, ViewType>::value, "The function requires std::string and std::string_view types.");

    auto result = StringType{text};
    std::transform(result.begin(), result.end(), result.begin(), toupper);
    return result;
}

template <typename StringType, typename ViewType>
StringType to_lower_case(ViewType text)
{
    static_assert(is_string<StringType, ViewType>::value, "The function requires std::string and std::string_view types.");

    auto result = StringType{text};
    std::transform(result.begin(), result.end(), result.begin(), tolower);
    return result;
}

실행 결과

Please enter any text: HelloWorld
to_upper_case: HELLOWORLD
to_lower_case: HELLOWORLD

활용팁

  • 간결한 코드 작성: C++17 이상을 사용한다면 _v 접미사가 붙은 변수 템플릿(예: std::is_same_v<T, U>)을 사용하여 코드를 더 간결하고 가독성 높게 만들 수 있습니다.
  • SFINAE 대체: C++11의 std::enable_if나 SFINAE(Substitution Failure Is Not An Error) 기법 대신 C++17의 if constexpr와 타입 특성을 함께 사용하면, 특정 타입 조건에 따라 다른 코드를 컴파일하도록 더 직관적으로 구현할 수 있습니다.
  • 개념(Concepts)과 함께 사용: C++20에 도입된 개념(Concepts)은 타입 특성을 기반으로 하여 템플릿 인자에 대한 제약을 훨씬 더 명확하고 간결하게 표현할 수 있게 해줍니다. 타입 특성은 개념을 구성하는 기본 요소로 활용됩니다.