Concepts and Type Traits

Concepts and Type Traits

Write clearer generic code with explicit constraints and standard type queries.

Concepts and Type Traits

Why concepts improve templates

Traditional templates often fail with long error messages far from the call site. Concepts let you declare what a type must support.

template <typename T>
concept Printable = requires(T value) {
    std::cout << value;
};

template <Printable T>
void print_line(const T& value) {
    std::cout << value << '\n';
}

This is easier to read than layers of enable_if.

Composing constraints

template <typename T>
concept SmallIntegral = std::integral<T> && (sizeof(T) <= 4);

Compose constraints when the combined name communicates intent better than raw expressions.

A more practical concept

template <typename T>
concept ReservableRange = requires(T container, std::size_t n) {
    container.reserve(n);
    container.begin();
    container.end();
};

template <ReservableRange T>
void preallocate_like(T& container, std::size_t count) {
    container.reserve(count);
}

This kind of constraint documents API expectations directly instead of burying them in template instantiation failures.

Useful type traits

if constexpr

template <typename T>
void describe(const T& value) {
    if constexpr (std::integral<T>) {
        std::cout << "integral\n";
    } else {
        std::cout << "non-integral\n";
    }
}

Use it when one generic implementation needs a few type-specific branches.

Traits and transformations together

template <typename T>
using value_type_t = typename std::remove_cvref_t<T>::value_type;

Traits are often most useful when they clean up dependent names or normalize incoming template arguments.

Practical advice

Exercises