On this page
article
C++ Type Traits
Type Traits 라이브러리
- 타입에 대한 속성 조사
- 변형된 타입을 구할 때 사용
- C++11에서 추가
예시: T가 포인터인지 조사
- C++11:
std::is_pointer<T>::value
- C++17:
std::is_pointer_v<T>
역사
- 1990년대 말부터 사용
- 2000년 Boost 라이브러리에서 제공
- C++11에서 표준으로 도입
is_pointer 구현 예시
// 기본 템플릿
template<typename T>
struct is_pointer {
enum { value = false };
};
// 포인터 특수화
template<typename T>
struct is_pointer<T*> {
enum { value = true };
};
meta function
- 컴파일러가 컴파일 시간에 사용하는 함수
- 컴파일 시간에 true/false 값을 적용
- 왜 enum 사용?
- 구조체 안에서 초기화 코드를 작성하고
- 컴파일 시간에 값을 알 수 있어야 함
- C++11 이후에는
static constexpr
로 대체 가능
if constexpr (C++17)
#include <iostream>
// is_pointer 정의
template<typename T>
struct is_pointer {
enum { value = false };
};
template<typename T>
struct is_pointer<T*> {
enum { value = true };
};
// printv 함수
template<typename T>
void printv(const T& value) {
if constexpr (is_pointer<T>::value)
std::cout << value << " : " << *value << std::endl;
else
std::cout << value << std::endl;
}
int main() {
int n = 10;
printv(&n); // OK
printv(n); // OK
}
int2type
- 개념: 컴파일 시간에 결정된 상수값을 타입으로 만들기
- C++11의
integral_constant
로 발전
// int2type
template<int N>
struct int2type {
enum { value = N };
};
// printv_imp 오버로딩
template<typename T>
void printv_imp(const T& value, int2type<1>) {
std::cout << value << " : " << *value << std::endl;
}
template<typename T>
void printv_imp(const T& value, int2type<0>) {
std::cout << value << std::endl;
}
// printv 함수
template<typename T>
void printv(const T& value) {
printv_imp(value, int2type<is_pointer<T>::value>());
}
integral_constant (C++11)
- 개념:
int
뿐만 아니라 다른 타입도 타입화 - 구현:
// <type_traits> 내부 구현 예시
template<typename T, T N>
struct integral_constant {
static constexpr T value = N;
using value_type = T;
using type = integral_constant;
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; }
};
// true_type, false_type 정의
using true_type = integral_constant<bool, true>;
using false_type = integral_constant<bool, false>;
// is_pointer 예시
template<typename T>
struct is_pointer : false_type {};
template<typename T>
struct is_pointer<T*> : true_type {};
장점
int2type
대신 true_type / false_type 오버로딩 사용하여 아래와 같이 printv_imp를 구현 가능
// printv_imp 오버로딩
template<typename T>
void printv_imp(const T& value, std::true_type) {
std::cout << value << " : " << *value << std::endl;
}
template<typename T>
void printv_imp(const T& value, std::false_type) {
std::cout << value << std::endl;
}
// printv 함수
template<typename T>
void printv(const T& value) {
printv_imp(value, std::is_pointer<T>());
}
is_pointer 구현 (const, volatile 대응)
namespace mystd {
namespace detail {
template<typename T>
struct is_pointer : std::false_type {};
template<typename T>
struct is_pointer<T*> : std::true_type {};
}
template<typename T>
using is_pointer = detail::is_pointer<std::remove_cv_t<T>>;
}
int main() {
namespace X = mystd;
std::cout << X::is_pointer<int*>::value << std::endl;
std::cout << X::is_pointer<int* const>::value << std::endl;
std::cout << X::is_pointer<int* volatile>::value << std::endl;
std::cout << X::is_pointer<int* const volatile>::value << std::endl;
}
is_pointer_v 구현 원리 (variable template)
template<typename T>
constexpr bool is_pointer_v = std::is_pointer<T>::value;
// 사용 예
bool b1 = std::is_pointer<int*>::value;
bool b2 = is_pointer_v<int*>;
포인터 여부에 따른 구현 방식 정리
if constexpr
사용 (C++17~)true_type
/false_type
오버로딩 (C++11~)enable_if
(C++11~)concept
(C++20~)
추가로 remove_all_pointer 구현
#include <iostream>
// 기본 템플릿
template<typename T>
struct remove_all_pointer {
using type = T;
};
// 포인터 특수화 (재귀)
template<typename T>
struct remove_all_pointer<T*> {
using type = typename remove_all_pointer<T>::type;
};
// alias template
template<typename T>
using remove_all_pointer_t = typename remove_all_pointer<T>::type;
int main() {
std::cout << typeid(remove_all_pointer<int*>::type).name() << std::endl;
std::cout << typeid(remove_all_pointer<int**>::type).name() << std::endl;
std::cout << typeid(remove_all_pointer<int***>::type).name() << std::endl;
}