Was Sie fragen, heißt multiple dispatch, aka Multimethoden. Es ist kein Feature der C++ - Sprache.
Es gibt Problemumgehungen für Sonderfälle, aber Sie können nicht vermeiden, selbst einige Implementierungen durchzuführen.
Ein häufiges Muster für Mehrfachversand heißt "Redispatch", auch bekannt als "rekursive verzögerte Dispatching". Grundsätzlich löst eine virtuelle Methode einen Parametertyp auf und ruft dann eine andere virtuelle Methode auf, bis alle Parameter aufgelöst sind. Die Funktion der Außenseite (wenn es eine gibt) ruft nur die erste dieser virtuellen Methoden auf.
Angenommen, es gibt n abgeleitete Klassen, es wird eine Methode geben, um den ersten Parameter aufzulösen, n um die zweite aufzulösen, n * n um die dritte aufzulösen - schlimmstenfalls sowieso. Das ist eine Menge manueller Arbeit, und die Verwendung von Bedingungsblöcken auf der Basis von typeid ist möglicherweise einfacher für die anfängliche Entwicklung, aber es ist robuster für die Wartung, Redispatch zu verwenden.
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
Implementierung dieser eine Vorlage für die abgeleiteten Klassen wäre schwierig, und die Metaprogrammierung zu handhaben es wahrscheinlich nicht lesbar, wartbaren und sehr zerbrechlich wäre. Wenn Sie den Versand mithilfe von Nicht-Vorlagenmethoden implementieren, ist die Verwendung einer Mixin-Vorlage (eine Vorlagenklasse, die die Basisklasse als Vorlagenparameter verwendet), um diese mit zusätzlichen Funktionen zu erweitern, möglicherweise nicht so schlecht.
Die visitor design pattern ist eng verwandt mit (im Grunde mit Redispatch IIRC implementiert).
Der andere Ansatz besteht darin, eine Sprache zu verwenden, die entwickelt wurde, um das Problem zu behandeln, und es gibt einige Optionen, die gut mit C++ funktionieren. Eine ist die Verwendung von treecc - eine domänenspezifische Sprache für die Behandlung von AST-Knoten und Operationen mit mehreren Zuständen, die, wie lex und yacc, "Quellcode" als Ausgabe erzeugen.
Um die Dispatch-Entscheidungen zu bearbeiten, werden nur Switch-Anweisungen generiert, die auf einer AST-Knoten-ID basieren. Dies kann genauso gut eine dynamisch typisierte Wertklassen-ID sein, IYSWIM. Dies sind jedoch Switch-Anweisungen, die Sie nicht schreiben oder pflegen müssen, was ein wesentlicher Unterschied ist. Das größte Problem, das ich habe, ist, dass AST-Knoten manipuliert werden, was bedeutet, dass Destruktoren für Mitgliedsdaten nicht aufgerufen werden, wenn Sie keine besondere Anstrengung unternehmen - dh es funktioniert am besten mit POD-Typen für Felder.
Eine andere Option ist die Verwendung eines Sprachvorprozessors, der Multimethoden unterstützt. Es gab einige davon, zum Teil, weil Stroustrup recht gut entwickelte Ideen zur Unterstützung von Multimethoden an einem Punkt hatte. CMM ist eins. Doublecpp ist ein anderer. Ein weiterer ist der Frost Project. Ich glaube, CMM ist dem am nächsten, was Stroustrup beschrieben hat, aber ich habe es nicht überprüft.
Letztendlich ist Multiple Dispatch jedoch nur eine Möglichkeit, eine Laufzeitentscheidung zu treffen, und es gibt viele Möglichkeiten, die gleiche Entscheidung zu treffen. Spezialisierte DSLs bringen eine Menge Ärger mit sich, also machen Sie das im Allgemeinen nur, wenn Sie vielfachen Versand benötigen. Redispatch und das Besuchermuster sind robuste WRT-Wartung, aber auf Kosten von etwas Komplexität und Unordnung. Einfache bedingte Anweisungen sind möglicherweise eine bessere Wahl für einfache Fälle, obwohl Sie sich bewusst sein sollten, dass das Erkennen der Möglichkeit eines unbehandelten Falls zur Kompilierungszeit dann schwierig, wenn nicht unmöglich ist.
Wie so oft gibt es keinen richtigen Weg, zumindest in C++.
Ich denke, Sie sollten erwähnen, dass Sie nicht den vollständigen Klassentyp kennen. Du kennst einfach die 'Base &'. Mehrere Antworten, einschließlich meiner eigenen, nahmen an, dass Sie den genauen Typ "Derived " kennen. –
Hinweis auf einen Kommentar zu einer Antwort: Eine zusätzliche Anforderung besteht darin, dass die Funktion keine Vorlage sein kann. es muss die angegebene Base * (Base &, Base &) Signatur haben. –
Eingeschlossen die Einschränkungen deutlicher in die Frage. –