0Pricing
C++ Academy · Lezione

L'idioma CRTP

Curiously recurring template pattern

L'idioma CRTP è una lezione C++ Academy gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C++ Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C++ Academy include 4 lezioni in totale.

Che cos'è CRTP?

Il Curiously Recurring Template Pattern (CRTP) è un idioma C++ in cui una classe Derived eredita da una classe base template istanziata con Derived stessa.

  • Forma: class Derived : public Base<Derived>
  • La classe base conosce il tipo derivato a tempo di compilazione
template <typename T>
class Base {};

class Derived : public Base<Derived> {};

Il meccanismo fondamentale

All'interno della classe base, è possibile applicare static_cast al puntatore this per convertirlo nel tipo derivato. Poiché il tipo derivato è un parametro di template, si tratta di una conversione interamente a tempo di compilazione, senza costi a runtime.

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

Perché "Curiously Recurring"?

Il nome deriva dal paradosso apparente: la classe base fa riferimento alla classe derivata, che non è ancora completamente definita quando viene scritto il template della classe base.

  • Funziona perché i template vengono istanziati solo quando sono utilizzati
  • Al momento dell'istanziazione, Derived è un tipo completo

Un primo utilizzo concreto

CRTP consente a una classe base di fornire funzionalità riutilizzabili che richiamano la classe derivata. Qui la classe base fornisce un print() che delega al name() della classe derivata.

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

Niente tabelle virtuali

A differenza del polimorfismo a runtime, CRTP non richiede la parola chiave virtual né un puntatore alla vtable in ogni oggetto. Il dispatch viene risolto interamente a tempo di compilazione.

  • Oggetti più piccoli (nessun vptr nascosto)
  • Le chiamate possono essere rese inline

Contare le istanze

Un'applicazione classica di CRTP è il conteggio degli oggetti. Ogni classe derivata ottiene automaticamente il proprio contatore statico.

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

Ogni classe derivata ha il proprio stato

Poiché Counter<Widget> e Counter<Gadget> sono istanziazioni distinte del template, ciascuna dispone di un count statico indipendente. Questa separazione è impossibile con un'unica classe base non template.

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

Qui static_cast è sicuro

Il downcast all'interno della classe base è sicuro solo se ogni oggetto di tipo Base<T> è effettivamente un T. CRTP lo garantisce per costruzione, poiché T è la classe che eredita.

  • Non si deve mai istanziare Base<Wrong> per una classe non correlata

Operatori di confronto con CRTP

È possibile generare un insieme completo di operatori di confronto a partire da un singolo operator<, ereditando da una classe helper per i confronti.

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

Proteggere il costruttore con protected

Per impedire l'uso accidentale della classe base, le si assegna un costruttore protected, in modo che solo le classi derivate possano crearla.

#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 nelle librerie standard

CRTP è presente in numerose librerie C++ reali.

  • std::enable_shared_from_this<T> lo utilizza
  • Molte librerie matematiche basate sugli expression template (Eigen) si fondano su di esso
  • Le librerie di range e gli helper per gli iteratori usano pattern simili

Riconoscere la forma X : Base<X> aiuta a leggere il codice avanzato.

Verifica rapida

Verifichi la Sua comprensione della forma CRTP.

Riepilogo

Ha imparato a conoscere l'idioma CRTP.

  • Derived : Base<Derived> fornisce alla classe base il suo tipo concreto
  • Un static_cast<T*>(this) abilita il dispatch a tempo di compilazione
  • Nessuna vtable, oggetti più piccoli e chiamate inline
  • Viene utilizzato per contatori, mixin di confronto e helper di libreria

Domande Frequenti

La lezione «L'idioma CRTP» è gratuita?

Sì — il testo completo di «L'idioma CRTP» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C++ Academy, passa a CoddyKit PRO. Il corso C++ Academy include 4 lezioni in totale.

Cosa imparerò in «L'idioma CRTP»?

Curiously recurring template pattern Eserciti C++ Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C++ Academy?

Non è richiesta alcuna esperienza precedente. C++ Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «L'idioma CRTP»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C++ Academy?

Sì. Ogni lezione C++ Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. L'idioma CRTP
  2. Polimorfismo statico
  3. Mixin con CRTP
  4. Quando usare CRTP
← Torna a C++ Academy