0Pricing
C++ Academy · Lektion

Das CRTP-Idiom

Curiously Recurring Template Pattern

Das CRTP-Idiom ist eine kostenlose C++ Academy-Lektion auf CoddyKit. Dies ist Lektion 1 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C++ Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C++ Academy-Kurs umfasst insgesamt 4 Lektionen.

Was ist CRTP?

Das Curiously Recurring Template Pattern (CRTP) ist ein C++-Idiom, bei dem eine Klasse Derived von einer mit Derived selbst instanziierten Template-Basisklasse erbt.

  • Form: class Derived : public Base<Derived>
  • Die Basisklasse kennt ihren abgeleiteten Typ zur Compilezeit
template <typename T>
class Base {};

class Derived : public Base<Derived> {};

Der grundlegende Mechanismus

In der Basisklasse können Sie den this-Zeiger mit static_cast in den abgeleiteten Typ umwandeln. Da der abgeleitete Typ ein Templateparameter ist, handelt es sich um eine vollständig zur Compilezeit aufgelöste Umwandlung ohne Laufzeitkosten.

#include <iostream>

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

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

int main() {
    Derived d;
    d.interface();
    return 0;
}

Warum "Curiously Recurring"?

Der Name geht auf das scheinbare Paradoxon zurück: Die Basisklasse verweist auf die abgeleitete Klasse, die beim Schreiben des Basistemplates noch nicht vollständig definiert ist.

  • Es funktioniert, weil Templates erst bei ihrer Verwendung instanziiert werden
  • Zum Zeitpunkt der Instanziierung ist Derived ein vollständiger Typ

Eine erste praktische Anwendung

Mit CRTP kann eine Basisklasse wiederverwendbare Funktionalität bereitstellen, die Aufrufe an die abgeleitete Klasse weiterleitet. Hier stellt die Basisklasse eine print()-Funktion bereit, die an name() der abgeleiteten Klasse delegiert.

#include <iostream>
#include <string>

template <typename T>
struct Named {
    void print() {
        std::cout << static_cast<T*>(this)->name() << "\n";
    }
};

struct Cat : Named<Cat> {
    std::string name() { return "Cat"; }
};

int main() {
    Cat c;
    c.print();
    return 0;
}

Keine virtuellen Tabellen

Im Gegensatz zur Laufzeitpolymorphie benötigt CRTP weder das Schlüsselwort virtual noch einen vtable-Zeiger in jedem Objekt. Die Auflösung des Aufrufs erfolgt vollständig zur Compilezeit.

  • Kleinere Objekte (kein versteckter vptr)
  • Aufrufe können inline expandiert werden

Instanzen zählen

Eine klassische Anwendung von CRTP ist ein Objektzähler. Jede abgeleitete Klasse erhält automatisch einen eigenen statischen Zähler.

#include <iostream>

template <typename T>
struct Counter {
    static inline int count = 0;
    Counter() { ++count; }
    ~Counter() { --count; }
};

struct Widget : Counter<Widget> {};

int main() {
    Widget a, b;
    std::cout << Widget::count << "\n";
    return 0;
}

Jeder abgeleitete Typ erhält seinen eigenen Zustand

Da Counter<Widget> und Counter<Gadget> unterschiedliche Templateinstanziierungen sind, besitzt jede Instanziierung einen unabhängigen statischen Wert count. Diese Trennung wäre mit einer einzigen nicht als Template ausgeführten Basisklasse nicht möglich.

#include <iostream>

template <typename T>
struct Counter {
    static inline int count = 0;
    Counter() { ++count; }
};

struct A : Counter<A> {};
struct B : Counter<B> {};

int main() {
    A a1, a2;
    B b1;
    std::cout << A::count << " " << B::count << "\n";
    return 0;
}

Der static_cast ist hier sicher

Die Abwärtskonvertierung innerhalb der Basisklasse ist nur dann sicher, wenn jedes Objekt vom Typ Base<T> tatsächlich ein T ist. CRTP garantiert dies durch seinen Aufbau, da T die erbende Klasse ist.

  • Instanziieren Sie niemals Base<Wrong> für eine unabhängige Klasse

Vergleichsoperatoren mit CRTP

Sie können aus einem einzigen operator< eine vollständige Gruppe von Vergleichsoperatoren ableiten, indem Sie von einer Vergleichshilfsklasse erben.

#include <iostream>

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

struct Num : Comparable<Num> {
    int v;
    Num(int x) : v(x) {}
    friend bool operator<(const Num& a, const Num& b) { return a.v < b.v; }
};

int main() {
    std::cout << (Num(3) > Num(2)) << "\n";
    return 0;
}

Schutz durch einen geschützten Konstruktor

Um eine versehentliche missbräuchliche Verwendung der Basisklasse zu verhindern, geben Sie ihr einen protected-Konstruktor, sodass nur abgeleitete Klassen sie instanziieren können.

#include <iostream>

template <typename T>
class Base {
protected:
    Base() = default;
public:
    void run() { static_cast<T*>(this)->step(); }
};

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

int main() {
    Job j;
    j.run();
    return 0;
}

CRTP in Standardbibliotheken

CRTP kommt in zahlreichen echten C++-Bibliotheken zum Einsatz.

  • std::enable_shared_from_this<T> verwendet es
  • Viele Bibliotheken für Mathematik mit Expression Templates (Eigen) nutzen es
  • Bereichs- und Iterator-Hilfsklassen verwenden ähnliche Muster

Wenn Sie die Form X : Base<X> erkennen, können Sie fortgeschrittenen Code leichter lesen.

Schnelltest

Testen Sie Ihr Verständnis der CRTP-Struktur.

Zusammenfassung

Sie haben das CRTP-Idiom kennengelernt.

  • Derived : Base<Derived> gibt der Basisklasse ihren konkreten Typ
  • Ein static_cast<T*>(this) ermöglicht die Auflösung zur Compilezeit
  • Keine vtable, kleinere Objekte und inline expandierbare Aufrufe
  • Es wird für Zähler, Vergleichs-Mixins und Bibliothekshilfen verwendet

Häufig gestellte Fragen

Ist die Lektion „Das CRTP-Idiom“ kostenlos?

Ja — der vollständige Text von „Das CRTP-Idiom“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C++ Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C++ Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Das CRTP-Idiom“?

Curiously Recurring Template Pattern Du übst C++ Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um C++ Academy zu starten?

Keine Vorkenntnisse erforderlich. C++ Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 1 von 4.

Wie lange dauert die Lektion „Das CRTP-Idiom“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser C++ Academy-Lektion Code schreiben und ausführen?

Ja. Jede C++ Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Das CRTP-Idiom
  2. Statischer Polymorphismus
  3. Mixins mit CRTP
  4. Wann Sie CRTP verwenden sollten
← Zurück zu C++ Academy