2013-03-23 8 views
6

Ich stoße auf ein Design-Problem, wo (in C++) möchte ich eine Vorlagen-Member-Funktion (einer Nicht-Vorlage-Klasse) virtuell sein und frage mich, ob es einen guten, eleganten Weg gibt.Wie um die Einschränkung zu entwerfen, dass Vorlagenfunktionen nicht virtuell sein können

Das Szenario geht, ich habe Maschinen dass Prozess generic Artikel. Ich verwende eine abstrakte Basisklasse für Maschinen mit einem virtuellen Prozess (Artikel) Funktion, so dass jede Maschine ihre eigene einzigartige Verarbeitungsmethode definieren kann. Das Problem ist, dass die Elemente auch "generisch" sind, da sie bestimmte Schnittstellen für die Verarbeitung offen legen. Aus Gründen (hauptsächlich für die Leistung ... keinen Vtable-Overhead) möchte ich für diese Elemente den Kompilierungs-Polymorphismus verwenden. So dass jetzt würde jede Maschine eine Schnittstelle wie hat:

class Machine 
{ public: 
    template <typename T> 
    virtual void process(T& item) = 0; 
}; 

jedoch dies in C++ nicht möglich ist, als Templat-Member-Funktionen können nicht virtuell sein. Natürlich kann ich die Maschinenklasse auf den Artikeltyp templated machen, aber das bringt mir mehr Kopfschmerzen in dem größeren Designschema und wirklich kein anderer Teil der Machine-Klasse hängt von Item ab ... es ist nur ein Argument für die process() - Funktion .

Gibt es einen besseren Weg um diese oder irgendwelche Vorschläge für die Bereitstellung dieser Art von generischen Familie von Maschinen, die eine Familie von generischen Elementen (wo die Elemente Compile-Zeit Polymorphismus verwenden). Bin ich in Bezug auf mein Design nicht am Ende?

Schätzen Anregungen

+0

So eine bestimmte Maschine kann Artikel aller Art verarbeiten? Und sind Item-Typen unzusammenhängend, oder könntest du eine "Item" -Superklasse machen? –

+1

Müssen Sie über einen Zeiger auf die abstrakte Klasse "Machine" auf eine konkrete Maschine zugreifen? Wenn nicht (wenn Sie über die konkrete Klasse darauf zugreifen können), ist [CRTP] (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) der richtige Weg (Stichwort: "Statischer Polymorphismus"). – leemes

+0

Sie haben hier zwei Möglichkeiten: CRTP und Doppelversand. Hier sagen Sie, dass Sie keinen doppelten Versand wünschen, aber dies könnte eine vorzeitige Optimierung sein (denken Sie darüber nach). –

Antwort

3

Typischerweise verwendet wird Double-Dispatch.

class Machine; 
class Item { 
public: 
    virtual void bounce(Machine& mach); 
}; 
class Machine { 
public: 
    template<typename T> void process(T& t); 
    virtual void process(Item& i) { 
     return i.bounce(*this); 
    } 
}; 
template<typename T> class CRTPItem { 
public: 
    virtual void bounce(Machine& mach) { 
     return mach.process(*(T*)this); 
    } 
}; 
class ConcreteItem : public CRTPItem<ConcreteItem> { 
public: 
    // blah blah 
}; 

In diesem Fall brauchen Sie keine virtuellen Overhead für die ganze ConcreteItem Schnittstelle, und sie nichts gemeinsam benötigen, nur dass man bounce Funktion, die von CRTPItem durch Erben automatisch für Sie erstellt wird. Dies sind nur zwei vtable-Aufrufe im Gegensatz zu denen, die Sie ursprünglich hatten, im Gegensatz zu einem Aufruf von vtable für alle Funktionen von Item, und die Schnittstelle kann weiterhin alle starken Schreibweisen behalten, die Sie hätten, wenn Sie virtuelle Vorlagen erstellen könnten.

+0

Ist 'CRTPItem <>' nicht von 'Item' abgeleitet? –

+0

Soll CTRPItem von Item abgeleitet werden und wie würde der Prozess einer DerivedMachine aufgerufen? (d. h. wie würde eine DerivedMachine aussehen)? –

+0

@DeadMG Sollte CTRPItem von Item abgeleitet werden und wie würde der Prozess einer DerivedMachine aufgerufen? (d. h. wie würde eine DerivedMachine aussehen)? –