Skip to content

Templates and Concepts

Templates define families of types or functions. Instantiation substitutes template arguments and checks the resulting program. Concepts name constraints and improve both interfaces and diagnostics.

Function template

template<typename T>
T maximum(T left, T right) {
    return left < right ? right : left;
}

This requires a usable < expression and compatible return behavior, but the unconstrained signature does not communicate that requirement.

Constrained template

#include <concepts>

template<std::totally_ordered T>
T maximum(T left, T right) {
    return left < right ? right : left;
}

A concept is a compile-time predicate over template arguments. It specifies syntactic and, by documented semantic requirements, behavioral expectations. The compiler can check expressions but cannot generally prove laws such as transitivity.

Custom concept

template<typename R>
concept SizedRange = requires(R const& range) {
    { range.size() } -> std::convertible_to<std::size_t>;
    range.begin();
    range.end();
};

Keep constraints at the abstraction level the algorithm actually needs. Requiring a concrete container where only a range is necessary reduces reuse.

Instantiation and visibility

Template definitions normally need to be visible where implicit instantiation occurs, which is why they commonly live in headers. Explicit instantiation can move selected generation to source files. Each specialization must still obey the One Definition Rule.

Class templates and deduction

template<typename T>
class box {
public:
    explicit box(T value) : value_{std::move(value)} {}
    T const& get() const noexcept { return value_; }

private:
    T value_;
};

box value{std::string{"notes"}}; // class template argument deduction

Static polymorphism and type erasure

Templates provide compile-time polymorphism and can inline concrete operations. Type erasure, as used by facilities such as std::function, provides a uniform runtime value interface without exposing the concrete type. Virtual interfaces, variants, templates, and type erasure have different extensibility, allocation, ABI, compile-time, and diagnostic trade-offs.

Exercises

  1. Constrain a mean function to a numeric input range and valid result type.
  2. Explain which semantic properties a compiler cannot infer from a concept.
  3. Compare a virtual strategy with a templated strategy.