// crtp_modern.cpp — compile with: c++ -std=c++20 -O2 crtp_modern.cpp // // CRTP (Curiously Recurring Template Pattern) shines when a base class needs // to return or operate on the *derived* type without paying for virtual // dispatch. Two practical mixins below. #include #include #include #include // --------------------------------------------------------------------------- // 1. Fluent-builder mixin // // Problem: chained setters in a base class return `Base&`, which loses the // derived type and breaks further chained calls to derived methods. // CRTP fix: the base knows the derived type and casts `*this` to it. // --------------------------------------------------------------------------- template class FluentBase { public: Derived& self() noexcept { return static_cast(*this); } Derived& named(std::string n) & { name_ = std::move(n); return self(); } const std::string& name() const noexcept { return name_; } private: std::string name_; }; class HttpRequest : public FluentBase { public: HttpRequest& method(std::string m) { method_ = std::move(m); return *this; } HttpRequest& url(std::string u) { url_ = std::move(u); return *this; } void send() const { std::cout << '[' << name() << "] " << method_ << ' ' << url_ << '\n'; } private: std::string method_, url_; }; // --------------------------------------------------------------------------- // 2. Arithmetic mixin // // Define `+=`, `-=`, `*=` in your type; the mixin synthesizes `+`, `-`, `*` // returning the derived type. No virtuals, no duplicated boilerplate per type. // --------------------------------------------------------------------------- template struct Arithmetic { friend Derived operator+(Derived a, const Derived& b) { a += b; return a; } friend Derived operator-(Derived a, const Derived& b) { a -= b; return a; } friend Derived operator*(Derived a, const Derived& b) { a *= b; return a; } }; struct Vec2 : Arithmetic { double x{}, y{}; Vec2& operator+=(const Vec2& o) { x += o.x; y += o.y; return *this; } Vec2& operator-=(const Vec2& o) { x -= o.x; y -= o.y; return *this; } Vec2& operator*=(const Vec2& o) { x *= o.x; y *= o.y; return *this; } friend std::ostream& operator<<(std::ostream& os, const Vec2& v) { return os << '(' << v.x << ", " << v.y << ')'; } }; // --------------------------------------------------------------------------- // Demo // --------------------------------------------------------------------------- int main() { HttpRequest{} .named("health-check") // FluentBase method, returns HttpRequest& .method("GET") // derived method still reachable .url("https://example.com/health") .send(); Vec2 a{1.0, 2.0}, b{3.0, 4.0}; std::cout << a + b << " | " << a * b << '\n'; }