[2026] C++ Type Traits | `<type_traits>` Complete Guide
이 글의 핵심
C++ type traits: `is_integral`, `remove_reference`, SFINAE with `enable_if`, `void_t`, and compile-time branches with `if constexpr`.
What are type traits?
Type traits are compile-time utilities that query and transform types. They enable metaprogramming and generic code that adapts to different types. 다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <type_traits>
#include <iostream>
template<typename T>
void process(T value) {
if constexpr (std::is_integral_v<T>) {
std::cout << "Processing integer: " << value << "\n";
} else if constexpr (std::is_floating_point_v<T>) {
std::cout << "Processing float: " << value << "\n";
} else {
std::cout << "Processing other type\n";
}
}
int main() {
process(42); // "Processing integer: 42"
process(3.14); // "Processing float: 3.14"
process("text"); // "Processing other type"
}
Basic type queries
Primary type categories
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <type_traits>
// Integral types
static_assert(std::is_integral_v<int>);
static_assert(std::is_integral_v<char>);
static_assert(std::is_integral_v<bool>);
static_assert(!std::is_integral_v<float>);
// Floating-point types
static_assert(std::is_floating_point_v<float>);
static_assert(std::is_floating_point_v<double>);
static_assert(!std::is_floating_point_v<int>);
// Pointer types
static_assert(std::is_pointer_v<int*>);
static_assert(std::is_pointer_v<char*>);
static_assert(!std::is_pointer_v<int>);
// Array types
static_assert(std::is_array_v<int[10]>);
static_assert(!std::is_array_v<int*>);
// Reference types
static_assert(std::is_reference_v<int&>);
static_assert(std::is_reference_v<int&&>);
static_assert(!std::is_reference_v<int>);
Composite type categories
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Arithmetic (integral or floating-point)
static_assert(std::is_arithmetic_v<int>);
static_assert(std::is_arithmetic_v<double>);
static_assert(!std::is_arithmetic_v<std::string>);
// Scalar (arithmetic, pointer, enum, nullptr_t)
static_assert(std::is_scalar_v<int>);
static_assert(std::is_scalar_v<int*>);
static_assert(!std::is_scalar_v<std::vector<int>>);
// Object types
static_assert(std::is_object_v<int>);
static_assert(std::is_object_v<std::string>);
static_assert(!std::is_object_v<int&>);
Type transformations
Remove qualifiers
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <type_traits>
// Remove const
using T1 = std::remove_const_t<const int>; // int
using T2 = std::remove_const_t<const int*>; // const int* (pointer itself not const)
// Remove volatile
using T3 = std::remove_volatile_t<volatile int>; // int
// Remove cv (const and volatile)
using T4 = std::remove_cv_t<const volatile int>; // int
// Remove reference
using T5 = std::remove_reference_t<int&>; // int
using T6 = std::remove_reference_t<int&&>; // int
using T7 = std::remove_reference_t<int>; // int
// Remove pointer
using T8 = std::remove_pointer_t<int*>; // int
using T9 = std::remove_pointer_t<int**>; // int*
Decay
아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 코드를 직접 실행해보면서 동작을 확인해보세요.
// Decay: array/function to pointer, remove cv and reference
using T1 = std::decay_t<int&>; // int
using T2 = std::decay_t<const int&>; // int
using T3 = std::decay_t<int[10]>; // int*
using T4 = std::decay_t<int(int)>; // int(*)(int)
Add qualifiers
아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Add const
using T1 = std::add_const_t<int>; // const int
// Add pointer
using T2 = std::add_pointer_t<int>; // int*
// Add lvalue reference
using T3 = std::add_lvalue_reference_t<int>; // int&
// Add rvalue reference
using T4 = std::add_rvalue_reference_t<int>; // int&&
Type relationships
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <type_traits>
// Same type
static_assert(std::is_same_v<int, int>);
static_assert(!std::is_same_v<int, long>);
static_assert(!std::is_same_v<int, int&>);
// Convertible
static_assert(std::is_convertible_v<int, double>);
static_assert(std::is_convertible_v<int*, void*>);
static_assert(!std::is_convertible_v<int, std::string>);
// Base of
class Base {};
class Derived : public Base {};
static_assert(std::is_base_of_v<Base, Derived>);
static_assert(!std::is_base_of_v<Derived, Base>);
SFINAE with enable_if
Function overloading
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <type_traits>
#include <iostream>
// For integral types
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
twice(T value) {
return value * 2;
}
// For floating-point types
template<typename T>
std::enable_if_t<std::is_floating_point_v<T>, T>
twice(T value) {
return value * 2.0;
}
int main() {
std::cout << twice(5) << "\n"; // 10 (integral version)
std::cout << twice(2.5) << "\n"; // 5.0 (floating version)
}
Return type SFINAE
아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
template<typename T>
auto getValue(T& container) -> decltype(container[0]) {
return container[0];
}
std::vector<int> vec = {1, 2, 3};
int first = getValue(vec); // OK: vector has operator[]
// std::list<int> lst = {1, 2, 3};
// auto x = getValue(lst); // Error: list doesn't have operator[]
if constexpr branches
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <type_traits>
#include <iostream>
#include <vector>
template<typename T>
void process(T value) {
if constexpr (std::is_integral_v<T>) {
std::cout << "Integer: " << value << "\n";
} else if constexpr (std::is_floating_point_v<T>) {
std::cout << "Float: " << value << "\n";
} else if constexpr (std::is_pointer_v<T>) {
std::cout << "Pointer: " << value << "\n";
} else {
std::cout << "Other type\n";
}
}
int main() {
process(42); // "Integer: 42"
process(3.14); // "Float: 3.14"
int x = 10;
process(&x); // "Pointer: 0x..."
process("hello"); // "Other type"
}
Custom traits with void_t
Detect member types
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <type_traits>
// Primary template
template<typename T, typename = void>
struct has_value_type : std::false_type {};
// Specialization for types with value_type
template<typename T>
struct has_value_type<T, std::void_t<typename T::value_type>>
: std::true_type {};
template<typename T>
inline constexpr bool has_value_type_v = has_value_type<T>::value;
// Usage
static_assert(has_value_type_v<std::vector<int>>);
static_assert(!has_value_type_v<int>);
Detect member functions
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <type_traits>
#include <utility>
// Detect if type has size() method
template<typename T, typename = void>
struct has_size : std::false_type {};
template<typename T>
struct has_size<T, std::void_t<
decltype(std::declval<T>().size())
>> : std::true_type {};
template<typename T>
inline constexpr bool has_size_v = has_size<T>::value;
// Usage
static_assert(has_size_v<std::vector<int>>);
static_assert(has_size_v<std::string>);
static_assert(!has_size_v<int>);
Detect container
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
template<typename T, typename = void>
struct is_container : std::false_type {};
template<typename T>
struct is_container<T, std::void_t<
typename T::value_type,
typename T::iterator,
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end()),
decltype(std::declval<T>().size())
>> : std::true_type {};
template<typename T>
inline constexpr bool is_container_v = is_container<T>::value;
// Usage
static_assert(is_container_v<std::vector<int>>);
static_assert(is_container_v<std::list<double>>);
static_assert(!is_container_v<int>);
Real-world examples
1. Generic serialization
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <type_traits>
#include <string>
#include <sstream>
template<typename T>
std::string serialize(const T& value) {
if constexpr (std::is_arithmetic_v<T>) {
return std::to_string(value);
} else if constexpr (std::is_same_v<T, std::string>) {
return "\"" + value + "\"";
} else if constexpr (std::is_pointer_v<T>) {
std::ostringstream oss;
oss << static_cast<const void*>(value);
return oss.str();
} else {
return "<unknown>";
}
}
// Usage
auto s1 = serialize(42); // "42"
auto s2 = serialize(3.14); // "3.140000"
auto s3 = serialize(std::string("hello")); // "\"hello\""
2. Optimized swap
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <type_traits>
#include <utility>
template<typename T>
void optimized_swap(T& a, T& b) {
if constexpr (std::is_trivially_copyable_v<T> && sizeof(T) <= 64) {
// Fast path for small trivial types
T temp = a;
a = b;
b = temp;
} else {
// Use move semantics for larger types
T temp = std::move(a);
a = std::move(b);
b = std::move(temp);
}
}
3. Type-safe printf
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <type_traits>
#include <iostream>
template<typename T>
void print_value(const T& value) {
if constexpr (std::is_integral_v<T>) {
std::cout << "%d: " << value;
} else if constexpr (std::is_floating_point_v<T>) {
std::cout << "%f: " << value;
} else if constexpr (std::is_pointer_v<T>) {
std::cout << "%p: " << value;
} else {
std::cout << value;
}
}
template<typename....Args>
void safe_printf(Args....args) {
(print_value(args), ...);
std::cout << "\n";
}
// Usage
safe_printf(42, 3.14, "hello");
Performance implications
Zero runtime cost: All type traits are evaluated at compile time. 아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// Both produce identical assembly
template<typename T>
T add(T a, T b) {
if constexpr (std::is_integral_v<T>) {
return a + b; // Integer addition
} else {
return a + b; // Floating-point addition
}
}
int x = add(5, 3); // Compiles to: add eax, ebx
double y = add(2.5, 1.5); // Compiles to: addsd xmm0, xmm1
Type traits vs C++20 Concepts
Type traits (C++11+)
아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
square(T x) {
return x * x;
}
Concepts (C++20+)
다음은 간단한 cpp 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
template<std::integral T>
T square(T x) {
return x * x;
}
Concepts advantages:
- More readable
- Better error messages
- Can be composed Type traits advantages:
- Works in C++11/14/17
- More flexible for complex conditions
Compiler support
| Compiler | <type_traits> | _v helpers | if constexpr |
|---|---|---|---|
| GCC | 4.3+ | 7+ (C++17) | 7+ (C++17) |
| Clang | 3.0+ | 3.9+ (C++17) | 3.9+ (C++17) |
| MSVC | 2010+ | 2015+ (C++17) | 2017+ (C++17) |
Related posts
- SFINAE
- enable_if
- if constexpr
- C++20 Concepts
- Template metaprogramming
Keywords
C++, type traits, SFINAE, templates, metaprogramming, compile-time, enable_if, if constexpr, void_t