Concepts and Type Traits
Concepts and Type Traits
Write clearer generic code with explicit constraints and standard type queries.
Concepts and Type Traits
Write clearer generic code with explicit constraints and standard type queries.
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.
It also fails closer to the call site. That matters because template diagnostics are only useful if readers can connect the failure to the actual API contract.
template <typename T>
concept SmallIntegral = std::integral<T> && (sizeof(T) <= 4);
Compose constraints when the combined name communicates intent better than raw expressions.
template <typename T>
concept SortableValue = std::totally_ordered<T> && std::movable<T>;
Good concept names read like design vocabulary, not like implementation trivia.
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.
template <typename T>
concept Validatable = requires(const T& value) {
{ value.validate() } -> std::same_as<bool>;
};
template <Validatable T>
bool all_valid(const std::vector<T>& values) {
for (const auto& value : values) {
if (!value.validate()) {
return false;
}
}
return true;
}
This is what concepts are for in real code: expressing the contract once so the algorithm can stay small and obvious.
Use concepts at the interface boundary and traits inside generic implementation details.
template <typename T>
void log_kind(const T& value) {
if constexpr (std::is_floating_point_v<T>) {
std::cout << "floating-point\n";
} else if constexpr (std::is_integral_v<T>) {
std::cout << "integral\n";
} else {
std::cout << "other\n";
}
}
The public API does not need a concept here because the function is willing to accept many kinds of types. Traits are the right tool for the internal branching.
std::is_same_v<T, U>std::is_integral_v<T>std::is_invocable_v<F, Args...>std::remove_cvref_t<T>std::conditional_t<cond, A, B>template <typename T>
void describe_value(T&& value) {
using Raw = std::remove_cvref_t<T>;
if constexpr (std::is_integral_v<Raw>) {
std::cout << "integral\n";
}
}
if constexprtemplate <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.
Prefer this over tag dispatch when the branching logic is small and local.
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.
requires expressions everywhere.reserve()" or "supports streaming".template <typename T>
concept StreamInsertable = requires(std::ostream& os, T value) {
os << value;
};
template <StreamInsertable T>
void print_debug(const T& value) {
std::cout << value << '\n';
}
If print_debug(widget) fails, start by checking whether widget actually supports operator<<. A named concept makes that diagnosis much faster than a deep template trace with no clear boundary.
if constexpr when one generic function only needs a few type-based branchespush_back() and size().std::integral and one for std::floating_point.template <typename T>
T twice(T value) {
return value + value;
}
int main() {
return twice(21);
}
Add a concept or `static_assert` so the template fails early for the wrong type. That is usually the first quality jump in generic code.
#include <concepts>
template <std::integral T>
T twice(T value) {
return value + value;
}