2016-08-07 30 views
0

Ich verwende das Visitor-Muster, um Reflektion zu implementieren, ohne auf RTTI angewiesen zu sein. Mein Problem ist:Variadic Template-Klasse: Ist es möglich, eine eindeutige Member-Funktion pro variadic Template-Argument zu implementieren?

ich einen Besucher implementieren will, die verschiedenen Klassen werfen kann DerivedItem1, DerivedItem2 usw. aus der gleichen BaseItem Klasse abgeleitet, auf diese BaseItem Klasse.

Die Basisklasse und einer der abgeleiteten Klassen wie folgt aussieht:

class BaseItem : public AbstractItem 
{ 
    virtual ~BaseItem(){} 
    virtual void visit(AbstractVisitor &v) 
    { 
     v.handle(*this); 
    } 
} 

class DerivedItem1 : public BaseItem 
{ 
    virtual ~DerivedItem(){} 
    virtual void visit(AbstractVisitor &v) 
    { 
     v.handle(*this); 
    } 
} 

Die Besucherklasse:

class BaseVisitor : public AbstractVisitor 
{ 
    virtual ~BaseVisitor(){} 
    void handle(BaseItem &item) 
    { 
     // <-- stuff to do for all classes derived from BaseItem 
    } 
} 

Es ist nicht möglich, den BaseVisitor so zu implementieren, seit DerivedItem::visit(BaseVisitor) wirft sich nicht auf seine Basisklasse und BaseVisitor::handle(BaseItem &v) wird nie aufgerufen werden.

Ich mag den Besucher als Template-Klasse implementieren, eine Basisklasse und alle abgeleiteten Klassen als Template-Parameter wie dies unter:

template <typename BaseT, typename... DerivedT> 
class BaseVisitor : public AbstractVisitor 
{ 
public: 
    virtual ~BaseVisitor(){} 

    // unpacking all DerivedT should happen here 
    // DerivedT_X are the packed template arguments ...DerivedT 
    void handle(DerivedT_1 &item) 
    { 
     // <-- cast item to BaseT, do stuff, return BaseT* to caller 
    } 

    void handle(DerivedT_2 &item) 
    { 
     // <-- cast item to BaseT, do stuff, return BaseT* to caller 
    } 
}; 

Ist es möglich, irgendwie mit C++, damit der Compiler dieses Mitglied erzeugen funktioniert für sich alleine?

+0

Sie könnten einen 'std :: tuple' von den variadische Template-Typen nehmen, und [iterieren diese] (http://stackoverflow.com/questions/1198260/iterate -über-tupel). –

+1

IINM, Andrei Alexandrescu hat ein Kapitel genau in [Modernes C++ Design] (https://en.wikipedia.org/wiki/Modern_C%2B%2B_Design). Sehen Sie [diesen Link] (http://www.artima.com/cppsource/cooperative_visitor.html) für etwas Ähnliches. –

+0

@AmiTavory Ah, sehr kreativ. Andrei ist wirklich gut in dieser Art von Template-Metaprogrammierung. –

Antwort

0

Sie können den Parameter Pack über den Körper der Template-Definition nicht auspacken, wie Sie in der Frage beschreiben wurden, aber Sie können CRTP verwenden, um eine Klasse zu definieren, die für die jeweils eine Hierarchie mit templatized Spezialisierungen erbt die Typ-Parameter liefern Sie:

#include <iostream> 

template<class L, class... R> struct X; 

template<class L> 
struct X<L> { void handle(L& i) { std::cout << i.f() << "\n"; } }; 

template<class L, class... R> 
struct X : public X<L>, public X<R...> { using X<L>::handle; using X<R...>::handle; }; 

struct A1 { 
    int f() { return 1; } 
}; 

struct A2 { 
    int f() { return 2; } 
}; 

struct B { 
    int f() { return 10; } 
}; 

struct B1 : public B { 
    int f() { return 11; } 
}; 

struct B2 : public B1 { 
    int f() { return 12; } 
}; 

int main() { 
    X<A1, A2> x1; 
    A1 a1; A2 a2; 
    x1.handle(a1); 
    x1.handle(a2); 

    X<B, B1, B2> x2; 
    B b; B1 b1; B2 b2; 
    x2.handle(b); 
    x2.handle(b1); 
    x2.handle(b2); 
} 
0

Mit CRTP und variadische Vorlage, können Sie so etwas wie:

// The generic visitor interface 
template <typename ... Ts> 
class IVisitor; 

template <> class IVisitor<> 
{ 
public: 
    virtual ~IVisitor() = default; 
}; 

template <typename T> class IVisitor<T> 
{ 
public: 
    virtual ~IVisitor() = default; 
    virtual void visit(const T&) = 0; 
}; 

template <typename T, typename...Ts> 
class IVisitor<T, Ts...> : IVisitor<T>, IVisitor<Ts...> 
{ 
public: 
    using IVisitor<T>::visit; 
    using IVisitor<Ts...>::visit; 
    virtual ~IVisitor() = default; 
}; 

// Helper for the concrete visitor using CRTP 
template <typename Derived, typename Base, typename...Ts> 
struct CRTPVisitorImpl; 

template <typename Derived, typename Base> 
struct CRTPVisitorImpl<Derived, Base> : Base {}; 

template <typename Derived, typename Base, typename T> 
struct CRTPVisitorImpl<Derived, Base, T> : virtual Base 
{ 
    using Base::visit; 
    void visit(const T& t) override { static_cast<Derived&>(*this).doVisit(t); }  
}; 

template <typename Derived, typename Base, typename T, typename ... Ts> 
struct CRTPVisitorImpl<Derived, Base, T, Ts...> : 
    CRTPVisitorImpl<Derived, Base, T>, 
    CRTPVisitorImpl<Derived, Base, Ts...> 
{ 
    using CRTPVisitorImpl<Derived, Base, T>::visit; 
    using CRTPVisitorImpl<Derived, Base, Ts...>::visit; 
}; 

// The generic Visitor 
template <typename Derived, typename Base> 
struct CRTPVisitor; 

template <typename Derived, typename ... Ts> 
struct CRTPVisitor<Derived, IVisitor<Ts...>> : 
    CRTPVisitorImpl<Derived, IVisitor<Ts...>, Ts...> 
{}; 

// Helper to write visited 
template <typename Derived, typename Base, typename Visitor> 
struct Visited : Base 
{ 
    void accept(Visitor& visitor) const override { 
     visitor.visit(static_cast<const Derived&>(*this)); 
    } 
}; 

Und Nutzung:

struct ShapeVisitorPrinter : CRTPVisitor<ShapeVisitorPrinter, IShapeVisitor> 
{ 
    template <typename T> 
    void doVisit(T&& t) const { 
     t.print(); 
    } 
}; 

jeder Ivisitor::visit Anruf doVisit mit CRTP, so müssen Sie nur jeden Fall decken über Vorlage/Überladung/Basisklasse.

Demo