C++ Academy · leksjon

CRTP-idiomet

Det merkelig tilbakevendende malmønsteret

Leksjon 1 av 413 trinn

CRTP-idiomet er en gratis leksjon i C++ Academy på CoddyKit. Dette er leksjon 1 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i C++ Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i C++ Academy inneholder totalt 4 leksjoner.

Hva er CRTP

Curiously Recurring Template Pattern (CRTP) er et C++-idiom der en klasse Derived arver fra en basisklassemal som er instansiert med selve Derived.

  • Form: class Derived : public Base<Derived>
  • Basisklassen kjenner den avledede typen ved kompilering
template <typename T>
class Base {};

class Derived : public Base<Derived> {};

Den grunnleggende mekanismen

Inne i basisklassen kan De bruke static_cast på this-pekeren for å konvertere den til den avledede typen. Siden den avledede typen er en malparameter, er dette en konvertering som utføres fullstendig ved kompilering, uten kjøretidskostnad.

#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;
}

Hvorfor «Curiously Recurring»

Navnet kommer fra det tilsynelatende paradokset: Basisklassen refererer til den avledede klassen, som ennå ikke er fullstendig definert når basismalen skrives.

  • Det fungerer fordi maler bare instansieres når de brukes
  • På instansieringstidspunktet er Derived en komplett type

En første praktisk bruk

CRTP lar en basisklasse tilby gjenbrukbar funksjonalitet som kaller tilbake til den avledede klassen. Her tilbyr basisklassen en print() som delegerer til den avledede klassens name().

#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;
}

Ingen virtuelle tabeller

I motsetning til kjøretidspolymorfi trenger CRTP verken nøkkelordet virtual eller en vtable-peker i hvert objekt. Oppslag av implementasjonen avgjøres fullstendig ved kompilering.

  • Mindre objekter (ingen skjult vptr)
  • Kall kan bygges inn

Telle instanser

En klassisk bruk av CRTP er en objektteller. Hver avledede klasse får automatisk sin egen statiske teller.

#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;
}

Hver avledede klasse får sin egen tilstand

Siden Counter<Widget> og Counter<Gadget> er ulike malinstansieringer, har hver av dem en uavhengig statisk count. Denne adskillelsen er umulig med én enkelt malfri basisklasse.

#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;
}

static_cast er trygt her

Nedkonverteringen inne i basisklassen er bare trygg hvis hvert objekt av typen Base<T> faktisk er en T. CRTP garanterer dette gjennom konstruksjonen, siden T er klassen som arver.

  • Ikke instansier Base<Wrong> for en ubeslektet klasse

Sammenligningsoperatorer med CRTP

De kan generere et komplett sett med sammenligningsoperatorer fra én enkelt operator< ved å arve fra en sammenligningshjelper.

#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;
}

Beskyttet konstruktørvakt

For å hindre utilsiktet feilbruk av basisklassen kan De gi den en protected-konstruktør, slik at bare avledede klasser kan opprette den.

#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 i standardbiblioteker

CRTP forekommer i mange virkelige C++-biblioteker.

  • std::enable_shared_from_this<T> bruker det
  • Mange matematikkbiblioteker med uttrykksmaler, for eksempel Eigen, er avhengige av det
  • Område- og iteratorhjelpere bruker lignende mønstre

Hvis De kjenner igjen formen X : Base<X>, blir det enklere å lese avansert kode.

Hurtigsjekk

Test forståelsen av CRTP-formen.

Oppsummering

De har lært CRTP-idiomet.

  • Derived : Base<Derived> gir basisklassen den konkrete typen
  • En static_cast<T*>(this) muliggjør oppslag av implementasjonen ved kompilering
  • Ingen vtable, mindre objekter og kall som kan bygges inn
  • Brukes til tellere, sammenligningsmiksiner og bibliotekshjelpere
Gratis å komme i gang

Lær deg C++ med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
51
Leksjoner
203

Ofte stilte spørsmål

Er leksjonen «CRTP-idiomet» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien C++ Academy, inkludert «CRTP-idiomet», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i C++ Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «CRTP-idiomet»?

Det merkelig tilbakevendende malmønsteret Du øver på C++ Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med C++ Academy?

Ingen tidligere erfaring er nødvendig. C++ Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 1 av 4.

Hvor lang tid tar leksjonen «CRTP-idiomet»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne C++ Academy-leksjonen?

Ja. Alle C++ Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. CRTP-idiomet
  2. Statisk polymorfisme
  3. Mixins med CRTP
  4. Når bør De bruke CRTP
← Tilbake til C++ Academy