C/C++ Arena

Step 5 of 9

Constraining templates with concepts

An unconstrained template accepts any type and fails deep inside its body with a wall of errors. A concept states the requirements up front, so a wrong type gets one clear error at the call site.

A concept can also require operations, not just categories like "integral". A requires expression lists expressions that must compile for the type, and optionally what they must return:

#include <concepts>
#include <iostream>
#include <string>
#include <vector>

template <typename T>
concept Priced = requires(const T& item) {
    { item.price() } -> std::convertible_to<double>;
    { item.label() } -> std::convertible_to<std::string>;
};

struct Book {
    double price() const { return 12.5; }
    std::string label() const { return "book"; }
};

struct Ticket {
    int cents;
    double price() const { return cents / 100.0; }
    const char* label() const { return "ticket"; }
};

template <Priced P>
double total(const std::vector<P>& items) {
    double sum = 0;
    for (const auto& i : items) sum += i.price();
    return sum;
}

int main() {
    std::cout << total(std::vector<Book>(2)) << "\n";
    std::cout << total(std::vector<Ticket>{{250}, {1000}}) << "\n";
    std::cout << Priced<Book> << Priced<int> << "\n";
}
25
12.5
10

How it works

Static polymorphism

Book and Ticket share no base class and have no virtual functions, yet total works for both. That's static polymorphism: the compiler generates a separate total for each type, with direct calls and zero run-time cost. Virtual functions (dynamic polymorphism) are still the tool when you need one container holding different types; concepts are for generic code where each call uses one type.

The concept for your task

template <typename T>
concept Shape = requires(const T& s) {
    { s.area() } -> std::convertible_to<double>;
    { s.name() } -> std::convertible_to<std::string>;
};

Your turn: write the Shape concept above and template <Shape S> double total_area(const std::vector<S>& shapes).

Previous: Writing your own trait Next: Value categories and reference collapsing