Menu
Coddy logo textTech

Mixins via CRTP

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 77 of 104.

The Curiously Recurring Template Pattern (CRTP) is a technique where a class inherits from a template base class, passing itself as the template argument. This enables compile-time polymorphism and allows base classes to access derived class members without virtual functions.

The basic CRTP structure looks like this:

template <typename Derived>
class Base {
public:
    void interface() {
        static_cast<Derived*>(this)->implementation();
    }
};

class MyClass : public Base<MyClass> {
public:
    void implementation() {
        std::cout << "MyClass implementation\n";
    }
};

CRTP is particularly powerful for creating mixins - reusable functionality that can be "mixed into" classes. Unlike traditional inheritance, mixins add capabilities without creating deep hierarchies:

template <typename Derived>
class Printable {
public:
    void print() const {
        const Derived& self = static_cast<const Derived&>(*this);
        std::cout << self.toString() << "\n";
    }
};

class Person : public Printable<Person> {
    std::string name;
public:
    Person(const std::string& n) : name(n) {}
    std::string toString() const { return "Person: " + name; }
};

// Usage:
Person p("Alice");
p.print();  // Output: Person: Alice

The key advantage is that all method calls are resolved at compile time, eliminating virtual function overhead. You can combine multiple CRTP mixins to compose functionality, making it a flexible alternative to runtime polymorphism when the types are known at compile time.

challenge icon

Challenge

Easy

Let's build a logging system that uses CRTP mixins to add reusable functionality to different classes without virtual function overhead. You'll create two mixins that can be "mixed into" any class: one for counting instances and one for generating string representations.

You'll organize your code across three files:

  • Mixins.h: Define two CRTP mixin templates that provide reusable functionality.

    Create a Countable mixin template that tracks how many instances of a derived class exist. It should have a static counter that increments in the constructor and decrements in the destructor. Provide a static method getCount() that returns the current count.

    Create a Describable mixin template that provides a describe() method. This method should use static_cast to access the derived class and call its getDescription() method, then print the result followed by a newline.

    Remember that CRTP mixins use static_cast<Derived*>(this) to access the derived class's members at compile time.

  • Entities.h: Define two entity classes that inherit from both mixins.

    Create a Player class that inherits from both Countable<Player> and Describable<Player>. It should store a name and level, and provide a getDescription() method that returns a string in the format: Player: [name] (Level [level])

    Create an Enemy class that also inherits from both mixins. It should store a type and health, and provide a getDescription() method that returns: Enemy: [type] with [health] HP

    Don't forget to initialize the static counter for each class.

  • main.cpp: Read four inputs (each on a separate line):
    1. Player name (string)
    2. Player level (integer)
    3. Enemy type (string)
    4. Enemy health (integer)

    Create a Player and an Enemy with the given values. Then demonstrate the mixins:

    1. Print Player count: [count] using the static getCount() method
    2. Print Enemy count: [count]
    3. Call describe() on the player
    4. Call describe() on the enemy
    5. Create a second player with name "Guest" and level 1
    6. Print Player count: [count] again to show the updated count
    7. Call describe() on the second player

For example, with inputs Hero, 10, Dragon, and 500:

Player count: 1
Enemy count: 1
Player: Hero (Level 10)
Enemy: Dragon with 500 HP
Player count: 2
Player: Guest (Level 1)

This challenge demonstrates how CRTP mixins add functionality (counting and describing) to unrelated classes without using virtual functions. Both Player and Enemy gain the same capabilities by inheriting from the same mixin templates, but each maintains its own separate instance counter because the template is instantiated with different types.

Cheat sheet

The Curiously Recurring Template Pattern (CRTP) enables compile-time polymorphism by having a class inherit from a template base class, passing itself as the template argument:

template <typename Derived>
class Base {
public:
    void interface() {
        static_cast<Derived*>(this)->implementation();
    }
};

class MyClass : public Base<MyClass> {
public:
    void implementation() {
        std::cout << "MyClass implementation\n";
    }
};

CRTP Mixins provide reusable functionality without deep inheritance hierarchies:

template <typename Derived>
class Printable {
public:
    void print() const {
        const Derived& self = static_cast<const Derived&>(*this);
        std::cout << self.toString() << "\n";
    }
};

class Person : public Printable<Person> {
    std::string name;
public:
    Person(const std::string& n) : name(n) {}
    std::string toString() const { return "Person: " + name; }
};

Key advantages:

  • All method calls resolved at compile time
  • No virtual function overhead
  • Multiple mixins can be combined to compose functionality
  • Each template instantiation maintains separate state (e.g., separate static counters per derived type)

Try it yourself

#include <iostream>
#include <string>
#include "Entities.h"

using namespace std;

int main() {
    // Read inputs
    string playerName;
    int playerLevel;
    string enemyType;
    int enemyHealth;
    
    cin >> playerName;
    cin >> playerLevel;
    cin >> enemyType;
    cin >> enemyHealth;
    
    // TODO: Create a Player with the given name and level
    
    // TODO: Create an Enemy with the given type and health
    
    // TODO: Print "Player count: [count]" using Player::getCount()
    
    // TODO: Print "Enemy count: [count]" using Enemy::getCount()
    
    // TODO: Call describe() on the player
    
    // TODO: Call describe() on the enemy
    
    // TODO: Create a second player with name "Guest" and level 1
    
    // TODO: Print "Player count: [count]" again
    
    // TODO: Call describe() on the second player
    
    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming