C/C++ Arena

CRTP: the curiously recurring template pattern

How a base class template that takes its derived class as a parameter gives static polymorphism, mixins and per-type counters without virtual functions.

In the Curiously Recurring Template Pattern, a class inherits from a template instantiated with itself: struct Version : Ordered<Version>. Inside the base, the derived type is known at compile time, so the base can call the derived class's functions with a static_cast, with no virtual functions and no vtable. The compiler sees the exact function and can inline it.

The price: Base<A> and Base<B> are unrelated types, so you can't keep an A and a B in one container. When you need that, use virtual functions or type erasure. C++20's <=> now covers the comparison case, and C++23's "deducing this" covers many others, but CRTP is everywhere in existing code.

Example

#include <iostream>

template <typename D>
struct Ordered {
    friend bool operator>(const D& a, const D& b) { return b < a; }
    friend bool operator<=(const D& a, const D& b) { return !(b < a); }
    friend bool operator>=(const D& a, const D& b) { return !(a < b); }
};

struct Version : Ordered<Version> {
    int major, minor;
    Version(int ma, int mi) : major(ma), minor(mi) {}
    friend bool operator<(const Version& a, const Version& b) {
        return a.major != b.major ? a.major < b.major : a.minor < b.minor;
    }
};

int main() {
    Version a{1, 4}, b{2, 0};
    std::cout << (b > a) << (a >= b) << (a <= a) << "\n";
    return 0;
}

Output:

101

Practice it