SyntaxStudy
Sign Up
C++ Compile-Time Polymorphism with CRTP
C++ Beginner 1 min read

Compile-Time Polymorphism with CRTP

C++ supports two forms of polymorphism: runtime polymorphism through virtual functions, and compile-time (static) polymorphism through templates. The Curiously Recurring Template Pattern (CRTP) achieves static polymorphism by having a derived class pass itself as a template argument to its base class. The base can then call derived class methods through a 'static_cast' to the template parameter, with zero runtime overhead because the binding occurs at compile time. CRTP is commonly used to implement mixins — base classes that add reusable behaviour to any class that inherits from them. Examples include adding comparison operators from a single 'operator<' definition, or providing a counting mechanism that tracks live instances of each concrete type independently. Because the base is templated on the derived type, each instantiation is a distinct class. The trade-off is that CRTP produces larger binaries due to template instantiation and is harder to read than virtual dispatch. Prefer virtual functions when runtime flexibility is needed. Use CRTP when the set of types is fixed at compile time and performance in tight loops matters, or when implementing library infrastructure such as iterator bases.
Example
#include <iostream>

// CRTP base that provides operator!= and operator> from operator== and operator<
template<typename Derived>
class Comparable {
public:
    bool operator!=(const Derived& rhs) const {
        return !(static_cast<const Derived&>(*this) == rhs);
    }
    bool operator>(const Derived& rhs) const {
        return rhs < static_cast<const Derived&>(*this);
    }
    bool operator<=(const Derived& rhs) const {
        return !(static_cast<const Derived&>(*this) > rhs);
    }
    bool operator>=(const Derived& rhs) const {
        return !(static_cast<const Derived&>(*this) < rhs);
    }
};

class Temperature : public Comparable<Temperature> {
public:
    explicit Temperature(double celsius) : celsius_(celsius) {}

    bool operator==(const Temperature& rhs) const {
        return celsius_ == rhs.celsius_;
    }
    bool operator<(const Temperature& rhs) const {
        return celsius_ < rhs.celsius_;
    }

    double value() const { return celsius_; }

private:
    double celsius_;
};

int main() {
    Temperature t1(20.0), t2(35.0);

    std::cout << std::boolalpha;
    std::cout << "t1 < t2:  " << (t1 < t2)  << "\n";  // true
    std::cout << "t1 > t2:  " << (t1 > t2)  << "\n";  // false
    std::cout << "t1 != t2: " << (t1 != t2) << "\n";  // true
    std::cout << "t1 <= t2: " << (t1 <= t2) << "\n";  // true

    return 0;
}