Search Knowledge

© 2026 LIBREUNI PROJECT

Modern C++ Programming / Metaprogramming

Type Traits and Compile-Time Programming

Type Traits: Compile-Time Type Information

The <type_traits> header provides utilities for querying and transforming types at compile time, enabling sophisticated template metaprogramming.

Type Predicates

Query type properties at compile time:

#include <type_traits>

static_assert(std::is_integral_v<int>);
static_assert(!std::is_integral_v<double>);

static_assert(std::is_floating_point_v<float>);
static_assert(std::is_pointer_v<int*>);
static_assert(std::is_array_v<int[10]>);
static_assert(std::is_class_v<std::string>);

Common Type Predicates

TraitChecks
is_voidType is void
is_integralInteger types
is_floating_pointfloat, double, long double
is_arithmeticIntegral or floating-point
is_pointerPointer type
is_referenceLvalue or rvalue reference
is_constConst-qualified
is_classClass or struct
is_enumEnumeration type

Type Relationships

static_assert(std::is_same_v<int, int>);
static_assert(!std::is_same_v<int, long>);

static_assert(std::is_base_of_v<Base, Derived>);
static_assert(std::is_convertible_v<Derived*, Base*>);

Type Transformations

Modify types at compile time:

// Remove const/volatile
using T1 = std::remove_const_t<const int>;  // int
using T2 = std::remove_cv_t<const volatile int>;  // int

// Add const/volatile
using T3 = std::add_const_t<int>;  // const int

// Remove reference
using T4 = std::remove_reference_t<int&>;  // int
using T5 = std::remove_reference_t<int&&>;  // int

// Remove pointer
using T6 = std::remove_pointer_t<int*>;  // int

// Decay (array/function to pointer, remove cv/reference)
using T7 = std::decay_t<int[10]>;  // int*
using T8 = std::decay_t<const int&>;  // int

Compile-Time Conditionals

std::conditional

Choose type based on condition:

template<typename T>
using StorageType = std::conditional_t<
    sizeof(T) <= 8,
    T,
    T*
>;

StorageType<int> x;      // int (small)
StorageType<BigClass> y; // BigClass* (large)

std::enable_if (SFINAE)

Enable function/class only if condition is true:

// Only enable for integral types
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
square(T value) {
    return value * value;
}

square(5);     // OK
// square(3.14);  // Error: SFINAE'd out

Constructibility and Assignability

static_assert(std::is_default_constructible_v<std::string>);
static_assert(std::is_copy_constructible_v<int>);
static_assert(std::is_move_constructible_v<std::unique_ptr<int>>);
static_assert(!std::is_copy_assignable_v<std::unique_ptr<int>>);

// Nothrow versions
static_assert(std::is_nothrow_move_constructible_v<std::string>);

Practical Example: Generic Swap

template<typename T>
void my_swap(T& a, T& b) noexcept(std::is_nothrow_move_constructible_v<T> &&
                                   std::is_nothrow_move_assignable_v<T>) {
    T temp = std::move(a);
    a = std::move(b);
    b = std::move(temp);
}

if constexpr (C++17)

Compile-time conditional branches:

template<typename T>
auto get_value(T t) {
    if constexpr (std::is_pointer_v<T>) {
        return *t;  // Dereference pointer
    } else {
        return t;   // Return value directly
    }
}

int x = 42;
auto v1 = get_value(&x);  // Dereferences, returns 42
auto v2 = get_value(x);   // Returns 42 directly

Discarded Branches

Code in discarded branches doesn’t need to be valid:

template<typename T>
void process(T value) {
    if constexpr (std::is_integral_v<T>) {
        std::cout << value * 2;  // Only instantiated for integral types
    } else {
        std::cout << value.toString();  // Only instantiated for types with toString()
    }
}

Tag Dispatching

Alternative to SFINAE using type tags:

template<typename Iterator>
void advance_impl(Iterator& it, int n, std::random_access_iterator_tag) {
    it += n;  // O(1) for random access
}

template<typename Iterator>
void advance_impl(Iterator& it, int n, std::input_iterator_tag) {
    while (n--) ++it;  // O(n) for input iterators
}

template<typename Iterator>
void advance(Iterator& it, int n) {
    advance_impl(it, n, typename std::iterator_traits<Iterator>::iterator_category{});
}

Compile-Time Assertions

template<typename T>
class Buffer {
    static_assert(std::is_trivially_copyable_v<T>,
                  "T must be trivially copyable");
    static_assert(sizeof(T) <= 64,
                  "T is too large for inline storage");
    T data_[100];
};

void_t: Detecting Members

template<typename, typename = void>
struct has_value_type : std::false_type {};

template<typename T>
struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type {};

static_assert(has_value_type<std::vector<int>>::value);
static_assert(!has_value_type<int>::value);
Conceptual Check

What does std::decay_t do to a type?

Runtime Environment

Interactive Lab

1#include <iostream>
2#include <type_traits>
3 
4template<typename T>
5void check_type(T value) {
6 if constexpr (std::is_integral_v<T>) {
7 std::cout << "Integral: " << value * 2 << '\n';
8 } else if constexpr (std::is_floating_point_v<T>) {
9 std::cout << "Floating: " << value / 2 << '\n';
10 } else {
11 std::cout << "Other type\n";
12 }
13}
14 
15int main() {
16 check_type(42);
17 check_type(3.14);
18 check_type("hello");
19
20 static_assert(std::is_same_v<std::decay_t<int&>, int>);
21 std::cout << "All assertions passed\n";
22
23 return 0;
24}
System Console

Waiting for signal...