L’idiome CRTP
Comprendre le motif de modèle à récurrence curieuse
L’idiome CRTP est une leçon C++ Academy gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C++ Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C++ Academy comprend 4 leçons au total.
Qu’est-ce que CRTP ?
Le modèle de programmation à récurrence curieuse (CRTP) est une idiome C++ dans lequel une classe Derived hérite d’une classe de base de modèle instanciée avec Derived elle-même.
- Forme :
class Derived : public Base<Derived> - La classe de base connaît son type dérivé à la compilation
template <typename T>
class Base {};
class Derived : public Base<Derived> {};Le mécanisme central
Dans la classe de base, vous pouvez appliquer static_cast au pointeur this pour obtenir le type dérivé. Comme le type dérivé est un paramètre de modèle, il s’agit d’une conversion entièrement effectuée à la compilation, sans coût à l’exécution.
#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;
}Pourquoi « récurrence curieuse » ?
Le nom vient d’un paradoxe apparent : la classe de base fait référence à la classe dérivée, qui n’est pas encore entièrement définie lorsque le modèle de base est écrit.
- Cela fonctionne parce que les modèles ne sont instanciés que lorsqu’ils sont utilisés.
- Au moment de l’instanciation,
Derivedest un type complet.
Une première utilisation concrète
CRTP permet à une classe de base de fournir des fonctionnalités réutilisables qui rappellent la classe dérivée. Ici, la classe de base fournit une fonction print() qui délègue l’appel à name() dans la classe dérivée.
#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;
}Aucune table virtuelle
Contrairement au polymorphisme à l’exécution, CRTP n’a besoin ni du mot-clé virtual ni d’un pointeur vers une table virtuelle dans chaque objet. La résolution de l’appel s’effectue entièrement à la compilation.
- Des objets plus petits, sans pointeur caché vers une table virtuelle
- Les appels peuvent être inline
Compter les instances
Une application classique de CRTP est le compteur d’objets. Chaque classe dérivée reçoit automatiquement son propre compteur statique.
#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;
}Chaque Derived possède son propre état
Comme Counter<Widget> et Counter<Gadget> sont des instanciations distinctes du modèle, chacune possède un count statique indépendant. Cette séparation est impossible avec une seule classe de base qui ne serait pas un modèle.
#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 est sûr ici
La conversion vers le type dérivé effectuée dans la classe de base est sûre uniquement si chaque objet de type Base<T> est réellement un T. CRTP le garantit par construction, puisque T est la classe qui hérite.
- N’instanciez jamais
Base<Wrong>pour une classe sans rapport.
Opérateurs de comparaison avec CRTP
Vous pouvez générer un ensemble complet d’opérateurs de comparaison à partir d’un seul operator< en héritant d’un utilitaire de comparaison.
#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;
}Protéger le constructeur
Pour empêcher toute mauvaise utilisation accidentelle de la classe de base, donnez-lui un constructeur protected, afin que seules les classes dérivées puissent l’instancier.
#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 dans les bibliothèques standard
CRTP apparaît dans de nombreuses bibliothèques C++ réelles.
std::enable_shared_from_this<T>l’utilise- de nombreuses bibliothèques mathématiques fondées sur des modèles d’expressions, comme Eigen, s’appuient dessus
- les plages et les utilitaires pour itérateurs utilisent des motifs similaires
Reconnaître la forme X : Base<X> vous aide à lire du code avancé.
Vérification rapide
Vérifiez votre compréhension de la forme CRTP.
Récapitulatif
Vous avez découvert l’idiome CRTP.
Derived : Base<Derived>fournit à la classe de base son type concret- Un
static_cast<T*>(this)permet la sélection à la compilation - Pas de table virtuelle, des objets plus petits et des appels pouvant être intégrés
- Utilisé pour les compteurs, les classes d’ajout de comparaison et les utilitaires de bibliothèques
Questions Fréquemment Posées
La leçon « L’idiome CRTP » est-elle gratuite ?
Oui — le texte complet de « L’idiome CRTP » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C++ Academy, passe à CoddyKit PRO. Le cours C++ Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « L’idiome CRTP » ?
Comprendre le motif de modèle à récurrence curieuse Tu pratiques C++ Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer C++ Academy ?
Aucune expérience préalable n'est requise. C++ Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.
Combien de temps prend la leçon « L’idiome CRTP » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon C++ Academy ?
Oui. Chaque leçon C++ Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.