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
- L'idioma CRTP
- Polimorfismo statico
- Mixin con CRTP
- Quando usare CRTP