Lo que está pidiendo que se llama multiple dispatch, también conocido como métodos múltiples. No es una característica del lenguaje C++.
hay soluciones para casos especiales, pero no se puede evitar hacer alguna aplicación a sí mismo.
Un patrón común para el envío múltiple se llama "reexpedición", también conocido como "despacho diferida recursiva". Básicamente, un método virtual resuelve un tipo de parámetros y luego llama a otro método virtual, hasta que se resuelvan todos los parámetros. La función del exterior (si hay una) simplemente llama al primero de estos métodos virtuales.
Suponiendo que hay n clases derivadas, habrá un método para resolver el primer parámetro, n para resolver el segundo, n * n para resolver el tercero y así sucesivamente, en el peor de todos modos. Esa es una buena cantidad de trabajo manual, y el uso de bloques condicionales basados en typeid podría ser más fácil para el desarrollo inicial, pero es más robusto para el mantenimiento para usar el redispatch.
class Base;
class Derived1;
class Derived2;
class Base
{
public:
virtual void Handle (Base* p2);
virtual void Handle (Derived1* p1);
virtual void Handle (Derived2* p1);
};
class Derived1 : public Base
{
public:
void Handle (Base* p2);
void Handle (Derived1* p1);
void Handle (Derived2* p1);
};
void Derived1::Handle (Base* p2)
{
p2->Handle (this);
}
void Derived1::Handle (Derived1* p1)
{
// p1 is Derived1*, this (p2) is Derived1*
}
void Derived1::Handle (Derived2* p1)
{
// p1 is Derived2*, this (p2) is Derived1*
}
// etc
La implementación de este uso de una plantilla para las clases derivadas sería difícil, y el metaprogramming plantilla para manejar probablemente sería ilegible, imposible de mantener y muy frágil. Sin embargo, implementar el envío utilizando métodos que no sean de plantilla, y luego usar una plantilla mixin (una clase de plantilla que toma su clase base como un parámetro de plantilla) para extender eso con características adicionales, puede no ser tan malo.
El visitor design pattern está estrechamente relacionado con (básicamente implementado con) redispatch IIRC.
El otro enfoque es utilizar un lenguaje diseñado para manejar el problema, y hay algunas opciones que funcionan bien con C++. Una es usar treecc, un lenguaje específico de dominio para manejar nodos AST y operaciones de despacho múltiple que, como lex y yacc, genera el "código fuente" como salida.
Todo lo que hace para manejar las decisiones de despacho es generar instrucciones de conmutación basadas en una ID de nodo AST, que también puede ser una ID de clase de valor de tipo dinámico, IYSWIM. Sin embargo, estas son declaraciones de cambio que no tiene que escribir o mantener, que es una diferencia clave. El mayor problema que tengo es que los nodos AST tienen manipulado su manejo de destructor, lo que significa que los destructores de los datos de los miembros no reciben llamadas a menos que haga un esfuerzo especial, es decir, funciona mejor con los tipos de POD para los campos.
Otra opción es usar un preprocesador de lenguaje que admita multimétodos. Ha habido algunos de estos, en parte porque Stroustrup tenía ideas bastante bien desarrolladas para soportar multimétodos en un punto. CMM es uno. Doublecpp es otro. Otra más es el Frost Project. Creo que CMM está más cerca de lo que Stroustrup describió, pero no lo he comprobado.
En última instancia, sin embargo, el envío múltiple es solo una forma de tomar una decisión en tiempo de ejecución, y hay muchas maneras de manejar la misma decisión. Los DSL especializados traen una gran cantidad de molestias, por lo que generalmente solo lo hace si necesita una gran cantidad de despachos múltiples. El redispatch y el patrón de visitantes son robustos para el mantenimiento de WRT, pero a expensas de cierta complejidad y desorden. Los enunciados condicionales simples pueden ser una mejor opción para casos simples, aunque tenga en cuenta que detectar la posibilidad de un caso no controlado en tiempo de compilación es difícil, si no imposible.
Como suele ser el caso, no hay una sola manera correcta de hacerlo, al menos en C++.
Creo que debería mencionar que no conoce el tipo de clase completo. Simplemente conoces 'Base &'. Varias respuestas, incluida la mía, asumieron que usted conoce el tipo exacto 'Derived '. –
Observando desde un comentario en una respuesta: Un requisito adicional es que la función no puede ser una plantilla; debe tener la firma Base * (Base &, Base &) dada. –
Incorporado las restricciones más claramente en la pregunta. –