可变参数模板 class:是否可以为每个可变参数模板参数实现一个唯一的成员函数?
Variadic template class: Is it possible to implement one unique member function per variadic template argument?
我在不依赖 RTTI 的情况下使用访问者模式来实现反射。
我的问题是:
我想实现一个访问者,它可以将不同的 classes DerivedItem1、DerivedItem2 等从相同的 BaseItem class 派生到这个 BaseItem class.
基础 class 和派生的 class 之一看起来像这样:
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:
class BaseVisitor : public AbstractVisitor
{
virtual ~BaseVisitor(){}
void handle(BaseItem &item)
{
// <-- stuff to do for all classes derived from BaseItem
}
}
不可能像这样实现 BaseVisitor,
因为 DerivedItem::visit(BaseVisitor)
不会将自己投射到它的 Base class
BaseVisitor::handle(BaseItem &v)
永远不会被调用。
我想将访问者实现为模板 class,将基础 class 和所有派生的 classes 作为模板参数,如下所示:
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++ 以某种方式让编译器自行生成此成员函数?
使用 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
,因此您只需通过 template/overload/base class.
涵盖每个案例
您不能像您在问题中描述的那样在模板定义的正文中解压缩参数包,但是您可以使用 CRTP 来 assemble 一个继承层次结构的 class您提供的每个类型参数的模板化特化:
#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);
}
我在不依赖 RTTI 的情况下使用访问者模式来实现反射。 我的问题是:
我想实现一个访问者,它可以将不同的 classes DerivedItem1、DerivedItem2 等从相同的 BaseItem class 派生到这个 BaseItem class.
基础 class 和派生的 class 之一看起来像这样:
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:
class BaseVisitor : public AbstractVisitor
{
virtual ~BaseVisitor(){}
void handle(BaseItem &item)
{
// <-- stuff to do for all classes derived from BaseItem
}
}
不可能像这样实现 BaseVisitor,
因为 DerivedItem::visit(BaseVisitor)
不会将自己投射到它的 Base class
BaseVisitor::handle(BaseItem &v)
永远不会被调用。
我想将访问者实现为模板 class,将基础 class 和所有派生的 classes 作为模板参数,如下所示:
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++ 以某种方式让编译器自行生成此成员函数?
使用 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
,因此您只需通过 template/overload/base class.
您不能像您在问题中描述的那样在模板定义的正文中解压缩参数包,但是您可以使用 CRTP 来 assemble 一个继承层次结构的 class您提供的每个类型参数的模板化特化:
#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);
}