Type Traits 1
개요
Type traits는 C++ 템플릿 메타프로그래밍의 핵심 기능 중 하나로, 컴파일 시간에 타입에 대한 정보를 질의하거나 타입을 변환하는 데 사용되는 템플릿 클래스 집합입니다. <type_traits>
헤더 파일에 정의되어 있으며, 이를 통해 코드의 유연성과 안정성을 크게 향상시킬 수 있습니다.
주요 용도는 다음과 같습니다.
- 타입 검사: 특정 타입이 정수형, 부동소수점형, 클래스 등 특정 카테고리에 속하는지 확인합니다.
- 타입 속성 확인: 타입이
const
한정자를 가졌는지, 포인터인지 등을 확인합니다. - 타입 관계 확인: 두 타입이 동일한지, 한 타입이 다른 타입으로 변환 가능한지 등을 확인합니다.
- 타입 변환: 타입에
const
를 추가하거나 포인터를 제거하는 등의 변환을 수행합니다.
특히 static_assert
와 결합하여 컴파일 타임에 템플릿 인자의 유효성을 검사하고, 조건에 맞지 않으면 컴파일 에러를 발생시켜 잘못된 사용을 조기에 방지하는 데 매우 유용합니다.
C++ 버전별 주요 키워드 도입 시기
- C++11:
<type_traits>
라이브러리의 대부분이 표준에 도입되었습니다.is_integral
,is_floating_point
,is_same
,enable_if
등 핵심적인 type traits가 포함되었습니다. - C++14:
_t
접미사를 사용하는 타입 별칭(alias) 템플릿이 추가되었습니다. (예:std::enable_if_t
) - C++17:
_v
접미사를 사용하는 변수 템플릿이 추가되어::value
없이 값을 바로 사용할 수 있게 되었습니다. (예:std::is_integral_v
). 또한,std::conjunction
,std::disjunction
,std::negation
과 같은 논리 연산자 traits가 추가되어 여러 조건을 조합하기 용이해졌습니다. - C++20: Concepts가 도입되어
type traits
를 이용한 SFINAE(Substitution Failure Is Not An Error) 기법보다 더 직관적이고 가독성 높은 방식으로 템플릿 제약 조건을 명시할 수 있게 되었습니다.
내용 설명
Type traits는 일반적으로 bool
타입의 value
멤버 상수를 가지는 형태로 구현됩니다. 이 값은 컴파일 타임에 결정되며, 조건이 참이면 true
, 거짓이면 false
가 됩니다.
예를 들어 std::is_integral<T>::value
는 T
가 정수 타입( int
, char
, short
등)일 경우 true
를, 아닐 경우 false
를 가집니다. C++17부터는 std::is_integral_v<T>
와 같이 _v
접미사를 붙여 더 간결하게 사용할 수 있습니다.
논리 연산 Traits (C++17)
여러 개의 type traits 조건을 조합할 때 유용한 논리 연산자 traits가 있습니다.
std::disjunction<Traits...>
: 논리합(OR) 연산. 나열된Traits
중 하나라도true
이면true
가 됩니다.std::conjunction<Traits...>
: 논리곱(AND) 연산. 나열된Traits
가 모두true
여야true
가 됩니다.
이를 사용하면 복잡한 static_assert
조건을 간결하게 표현할 수 있습니다.
예제 코드
#include <iostream>
#include <type_traits>
// is_numeric: T가 정수 또는 부동소수점 타입인지 확인하는 사용자 정의 trait
// std::disjunction: 여러 trait 중 하나라도 참이면 참 (OR 연산)
template <typename T>
struct is_numeric
: public std::disjunction<std::is_integral<T>, std::is_floating_point<T>>
{
};
// max1: C++17의 std::disjunction_v를 사용하여 숫자 타입인지 검사
template <typename T>
auto max1(const T &a, const T &b)
{
// static_assert: 컴파일 타임에 조건을 검사하여 만족하지 않으면 컴파일 에러 발생
static_assert(
std::disjunction_v<std::is_integral<T>, std::is_floating_point<T>>,
"max1: T must be an integral or floating-point type.");
return (a < b) ? b : a;
}
// max2: 사용자 정의 trait인 is_numeric을 사용하여 검사
template <typename T>
auto max2(const T &a, const T &b)
{
static_assert(is_numeric<T>::value, "max2: T must be a numeric type.");
return (a < b) ? b : a;
}
// max3: 두 개의 다른 타입을 받아 각각 숫자 타입인지 검사
// std::conjunction_v: 여러 trait가 모두 참이어야 참 (AND 연산)
template <typename T, typename U>
auto max3(const T &a, const U &b)
{
static_assert(std::conjunction_v<is_numeric<T>, is_numeric<U>>,
"max3: Both T and U must be numeric types.");
// 다른 타입 간의 비교를 위해 공통 타입으로 변환 후 비교
using CommonType = std::common_type_t<T, U>;
return (static_cast<CommonType>(a) < static_cast<CommonType>(b)) ? static_cast<CommonType>(b) : static_cast<CommonType>(a);
}
int main()
{
std::cout << "--- max1 (single type) ---" << std::endl;
std::cout << max1(10, 11) << std::endl;
std::cout << max1(10.0F, 11.0F) << std::endl;
std::cout << max1(10.0, 11.0) << std::endl;
std::cout << max1<short>(10, 11) << std::endl;
// max1("a", "b"); // 컴파일 에러 발생
std::cout << "\n--- max2 (using custom trait) ---" << std::endl;
std::cout << max2(10, 11) << std::endl;
std::cout << max2(10.0F, 11.0F) << std::endl;
std::cout << max2(10.0, 11.0) << std::endl;
std::cout << max2<short>(10, 11) << std::endl;
std::cout << "\n--- max3 (multiple types) ---" << std::endl;
std::cout << max3(10, 11.0) << std::endl; // int, double -> double
std::cout << max3(12.5f, 11) << std::endl; // float, int -> float
return 0;
}
실행 결과
--- max1 (single type) ---
11
11
11
11
--- max2 (using custom trait) ---
11
11
11
11
--- max3 (multiple types) ---
11
12.5
활용팁
- SFINAE(Substitution Failure Is Not An Error):
std::enable_if
와 같은 type traits를 사용하여 특정 조건이 만족될 때만 템플릿 함수나 클래스가 오버로드 확인 대상에 포함되도록 할 수 있습니다. 이는 복잡한 템플릿 오버로딩을 구현할 때 강력한 도구입니다. - 코드 최적화:
std::is_trivially_copyable
과 같은 traits를 사용하여 타입의 특성에 따라memcpy
와 같은 빠른 저수준 함수를 사용할지, 아니면 복사 생성자를 호출할지 결정하는 등 런타임 성능을 최적화할 수 있습니다. - 가독성 및 유지보수: 템플릿 코드에
static_assert
와 type traits를 적극적으로 사용하면, 템플릿을 사용하는 개발자에게 명확한 제약 조건을 알려주고, 의도치 않은 타입으로 템플릿이 인스턴스화되는 것을 막아 디버깅이 어려운 컴파일 에러를 방지할 수 있습니다. - C++20 Concepts: C++20 이상을 사용한다면,
requires
절과 Concepts를 사용하여 type traits 기반의 제약 조건을 훨씬 더 간결하고 명확하게 표현하는 것을 권장합니다. Concepts는 내부적으로 type traits를 사용하여 구현되는 경우가 많습니다.
'개발 > C++ (98,03,11,14,17,20,23)' 카테고리의 다른 글
Modern C++ : 템플릿 특수화 (Template Specialization) (98, 11, 17) (0) | 2025.09.23 |
---|---|
Modern C++ : 복사 및 이동 의미론 (Copy and Move Semantics) (98, 11) (0) | 2025.09.22 |
Modern C++ : 연산자 오버로딩 (Operator Overloading) (98, 11, 20) (0) | 2025.09.21 |
Modern C++ : 클래스 템플릿(Class Template) (98) (0) | 2025.09.20 |
Modern C++ : 다형성(Polymorphism) (98, 11) (0) | 2025.09.19 |
Modern C++ : 상속(Inheritance) (98, 11) (0) | 2025.09.18 |
Modern C++ : 클래스(Class) (98, 11, 17) (0) | 2025.09.17 |
Modern C++ : 유틸리티 함수 활용 (11, 17) (1) | 2025.09.15 |