Comment puis-je utiliser la CRTP en C++ pour éviter la surcharge des fonctions membres virtuelles ?
Réponses
Trop de publicités?Il y a deux façons de procéder.
La première consiste à spécifier l'interface de manière statique pour la structure des types :
template <class Derived>
struct base {
void foo() {
static_cast<Derived *>(this)->foo();
};
};
struct my_type : base<my_type> {
void foo(); // required to compile.
};
struct your_type : base<your_type> {
void foo(); // required to compile.
};
La seconde consiste à éviter l'utilisation de l'idiome référence-à-base ou pointeur-à-base et à effectuer le câblage au moment de la compilation. En utilisant la définition ci-dessus, vous pouvez avoir des fonctions modèles qui ressemblent à celles-ci :
template <class T> // T is deduced at compile-time
void bar(base<T> & obj) {
obj.foo(); // will do static dispatch
}
struct not_derived_from_base { }; // notice, not derived from base
// ...
my_type my_instance;
your_type your_instance;
not_derived_from_base invalid_instance;
bar(my_instance); // will call my_instance.foo()
bar(your_instance); // will call your_instance.foo()
bar(invalid_instance); // compile error, cannot deduce correct overload
Ainsi, la combinaison de la définition de la structure/interface et de la déduction de type au moment de la compilation dans vos fonctions vous permet d'effectuer un dispatch statique au lieu d'un dispatch dynamique. C'est l'essence même du polymorphisme statique.
J'ai moi-même cherché des discussions décentes sur le CRTP. L'article de Todd Veldhuizen Techniques pour le C++ scientifique est une excellente ressource pour cela (1.3) et de nombreuses autres techniques avancées comme les modèles d'expression.
J'ai également découvert que vous pouviez lire la majeure partie de l'article original de Coplien sur C++ Gems sur Google books. Peut-être que c'est toujours le cas.
J'ai dû chercher CRTP . Cependant, après avoir fait cela, j'ai trouvé des informations sur Polymorphisme statique . Je soupçonne que c'est la réponse à votre question.
Il s'avère que ATL utilise ce modèle de manière assez intensive.
Ce site La réponse de Wikipedia contient tout ce dont vous avez besoin. A savoir :
template <class Derived> struct Base
{
void interface()
{
// ...
static_cast<Derived*>(this)->implementation();
// ...
}
static void static_func()
{
// ...
Derived::static_sub_func();
// ...
}
};
struct Derived : Base<Derived>
{
void implementation();
static void static_sub_func();
};
Bien que je ne sache pas combien cela vous rapporte réellement. L'overhead d'un appel de fonction virtuelle est (dépendant du compilateur, bien sûr) :
- Mémoire : Un pointeur de fonction par fonction virtuelle
- Durée d'exécution : Un appel de pointeur de fonction
Alors que l'overhead du polymorphisme statique CRTP est :
- Mémoire : Duplication de la base par instanciation de modèle
- Durée d'exécution : Un appel de pointeur de fonction + ce que fait static_cast.