【问题标题】:Variadic template class: Is it possible to implement one unique member function per variadic template argument?可变参数模板类:是否可以为每个可变参数模板参数实现一个唯一的成员函数?
【发布时间】:2016-08-07 18:50:50
【问题描述】:

我正在使用访问者模式来实现反射,而不依赖于 RTTI。 我的问题是:

我想实现一个访问者,它可以将派生自同一个 BaseItem 类的不同类 DerivedItem1、DerivedItem2 等转换为这个 BaseItem 类。

基类和其中一个派生类如下所示:

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) 不会将自己转换为它的基类 并且BaseVisitor::handle(BaseItem &amp;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++ 让编译器自行生成此成员函数?

【问题讨论】:

标签: c++ variadic-templates template-classes


【解决方案1】:

您不能像在问题中描述的那样在模板定义的主体中解压缩参数包,但您可以使用 CRTP 组装一个类,该类继承一个层次结构,其中每个类型参数的模板化特化供应:

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

【讨论】:

    【解决方案2】:

    使用 CRTP 和可变参数模板,您可以执行以下操作:

    // 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 都使用 CRTP 调用 doVisit,因此您只需通过模板/重载/基类覆盖每种情况。

    Demo

    【讨论】:

      猜你喜欢
      • 2015-03-12
      • 1970-01-01
      • 2021-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多