2016-10-16 3 views
2

Hinweis: Die folgende Frage bezieht sich auf Template Method Design Pattern und C++ - Funktionsvorlagen. Um beide zu unterscheiden, werde ich kursiv verwenden, wenn Sie auf das Entwurfsmuster verweisen, und fett, wenn Sie auf C++ - Vorlagen verweisen.Implementieren Vorlagenvorlage Methode

Die Idee der Vorlage Methode Muster ist, Teile eines Algorithmus austauschbar zu machen. Dies wird normalerweise durch Vererbung erreicht, wobei die Unterklasse konkrete Implementierungen bereitstellt, die in einen Algorithmus der Basisklasse eingesteckt werden. Wenn die Hook-Methoden jedoch Vorlagen sein müssen, funktioniert dies nicht, da Vorlagen nicht virtuell sein können. Hier ist ein einfaches Beispiel, das nicht kompilieren lässt:

class Base 
{ 
public: 

    // This is the template method 
    template <typename T> 
    void doSomething(T input) 
    { 
     //... 
     auto converted = ConvertInput(input); 
     //... 
     std::cout << converted; 
    } 

protected: 
    //compile error "member function templates cannot be virtual" 
    template <typename T> 
    virtual T ConvertInput(T input) = 0; 
}; 

class Derived : public Base 
{ 
protected: 
    template <typename T> 
    T ConvertInput(T input) 
    { 
     return 2 * input; 
    } 
}; 

int main() 
{ 
    Derived d; 
    d.doSomething(3); 
} 

Gibt es eine Möglichkeit Vorlage Methoden die Funktionsschablone Haken verwenden zu implementieren?

Ich bin nicht daran interessiert, die Base Klasse als Typ irgendwo zu verwenden. Ich werde immer den konkreten Typ verwenden, um eine maximale Kompilierzeitoptimierung zu erreichen. Also eine andere Formulierung dieser Frage ist: Wie kann ich mehrere Klassen Derived-1 .. Derived-n erstellen, die Funktionsvorlagen haben, die ein gemeinsames Code-Skelett über die Implementierungen teilen?

+3

[CRTP] (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). Nehmen wir an, dass 'Base'' Derived' als Template-Parameter verwendet. Call 'ConvertInput' via' static_cast (this) -> ConvertInput' –

+0

Gibt es eine Beschränkung für die Menge der für 'T' verwendeten Typen? – Yakk

+0

@Yakk Ja, aber nur implizite. Z.B. Eine Instanz davon benötigt Iteratoren, was alles sein kann. –

Antwort

3

Klingt wie eine feine Anwendung für CRTP. Definieren Sie Base als Klassenvorlage mit dem daraus abgeleiteten Typ als Vorlageparameter. Innerhalb Base ‚s Methoden können Sie auf der abgeleiteten Typ niedergeschlagen:

template<typename Derived> 
struct Base 
{ 
    // This is the template method 
    template <typename T> 
    void doSomething(T input) 
    { 
     //... 
     auto converted = static_cast<Derived*>(this)->ConvertInput(input); 
     //... 
     std::cout << converted << std::endl; 
    } 
}; 

Und dann die abgeleiteten Typen definieren, zum Beispiel:

struct Square : Base<Square> 
{ 
    template<typename T> 
    auto ConvertInput(T t) 
    { 
     return t*t; 
    } 
}; 

struct Sum : Base<Sum> 
{ 
    template<typename T> 
    auto ConvertInput(T t) 
    { 
     return t+t; 
    } 
}; 

die Nutzung ist ziemlich trivial:

Square sq; 
Sum sum; 
sq.doSomething(3); 
sum.doSomething(3); 

live demo

+0

Danke. Ich habe schon in diese Richtung gedacht und es ist definitiv eine mögliche Lösung. Ich wäre auch an Lösungen interessiert, die nicht erfordern, dass "Base" eine Klassenvorlage erstellt. –

+1

+1 für CRTP: habe das nicht gelesen. Wenn der Rest der Post Sie verwirrt, lesen Sie einfach über CRTP, Ihr Problem ist perfekt dafür. – Yakk

2

CRTP löst Ihr Problem von Maki ng Erstellen Sie eine Vorlage.

Wenn T aus einem endlichen Satz stammt oder die Konvertierung nicht arbuträr ist, kann der Typ löschen funktionieren.

Wenn eine endliche Menge, löschen Sie alle abgeleiteten virtuellen Methoden. Wenn Sie eine gemeinsame Eigenschaft haben, geben Sie diese Eigenschaft löschen und die Methode, die darauf wirkt, virtualisieren. Oder eine Mischung.

Andernfalls kann Base über Template-Methoden verfügen, die den Vorgang als Funktionsobjekt (mit Vorlage operator()) statt mit virtual verwenden, um ihn zu finden. Derived übergibt die Vorlagenoperationen als Argumente an die Base-Methode (n). Dies ist im Grunde CRTP ohne CRTP.

Verwandte Themen