0

RTTI に依存せずにリフレクションを実装するために Visitor パターンを使用しています。私の問題は次のとおりです。

同じ BaseItem クラスから派生したさまざまなクラス DerivedItem1、DerivedItem2 などをこの BaseItem クラスにキャストできる Visitor を実装したいと考えています。

基本クラスと派生クラスの 1 つは次のようになります。

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);
    }
}

訪問者クラス:

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

このように BaseVisitor を実装することはできません。これはDerivedItem::visit(BaseVisitor)、Base クラスに自分自身をキャストせず、BaseVisitor::handle(BaseItem &v)呼び出されないためです。

次のように、基本クラスとすべての派生クラスをテンプレート パラメーターとして使用して、ビジターをテンプレート クラスとして実装したいと考えています。

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
    }
};

C++ を使用して、コンパイラがこのメンバー関数を独自に生成できるようにすることは可能ですか?

4

2 に答える 2

0

You can't unpack the parameter pack across the body of the template definition as you were describing in the question, but you can use CRTP to assemble an class that inherits a hierarchy with templatized specializations for each of the type-parameters you supply:

#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);
}
于 2016-08-07T23:18:32.920 に答える
0

CRTP と variadic テンプレートを使用すると、次のようなことができます。

// 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));
    }
};

そして使用法:

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

Ivisitor::visit呼び出しdoVisitは CRTP を使用するため、テンプレート/オーバーロード/基本クラスを介して各ケースをカバーする必要があります。

デモ

于 2016-08-07T22:48:20.223 に答える