¿Cómo puedo usar CRTP en C ++ para evitar la sobrecarga de las funciones de miembros virtuales?
¿Cómo puedo usar CRTP en C ++ para evitar la sobrecarga de las funciones de miembros virtuales?
Respuestas:
Hay dos maneras.
El primero es especificando la interfaz estáticamente para la estructura de tipos:
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 segunda es evitar el uso del lenguaje de referencia a base o puntero a base y hacer el cableado en tiempo de compilación. Usando la definición anterior, puede tener funciones de plantilla que se parecen a estas:
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
Por lo tanto, la combinación de la definición de estructura / interfaz y la deducción de tipo en tiempo de compilación en sus funciones le permite realizar un envío estático en lugar de un envío dinámico. Ésta es la esencia del polimorfismo estático.
not_derived_from_base
no se deriva base
ni se deriva de base
...
Yo mismo he estado buscando discusiones decentes sobre CRTP. Las técnicas de Todd Veldhuizen para C ++ científico son un gran recurso para esto (1.3) y muchas otras técnicas avanzadas como las plantillas de expresión.
Además, descubrí que se podía leer la mayoría de los artículos de Gemas C ++ originales de Coplien en Google Books. Quizás ese todavía sea el caso.
dynamic_cast
o métodos virtuales.
Tuve que buscar CRTP . Sin embargo, habiendo hecho eso, encontré algunas cosas sobre el polimorfismo estático . Sospecho que esta es la respuesta a tu pregunta.
Resulta que ATL usa este patrón de manera bastante extensa.
Esta respuesta de Wikipedia tiene todo lo que necesita. A saber:
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();
};
Aunque no sé cuánto te compra esto realmente. La sobrecarga de una llamada de función virtual es (dependiente del compilador, por supuesto):
Mientras que la sobrecarga del polimorfismo estático CRTP es: