如何编写CRTP模板自动实现多Group的Visitor类?
问题:实现满足特定要求的CRTP模板
GroupsVisitor 现有类结构
enum class Group { A, B }; template <Group G> class AbstractGroupVisitor; template <Group G> class GroupMessage; class AbstractMessageVisitor; class AbstractMessage { public: virtual void accept(AbstractMessageVisitor& visitor) = 0; }; class AbstractMessageVisitor { public: virtual void visit(GroupMessage<Group::A>& msg) = 0; virtual void visit(GroupMessage<Group::B>& msg) = 0; // 针对Group枚举中每个成员的纯虚方法 }; template <Group G> class GroupMessage: public AbstractMessage { public: void accept(AbstractMessageVisitor& visitor) final { visitor.visit(*this); } virtual void accept(AbstractGroupVisitor<G>& visitor) = 0; };
需求与问题
能否编写如下CRTP模板:
template <Group... Gs> class GroupsVisitor;
使其满足以下要求:
- 继承自
AbstractMessageVisitor - 为
Gs参数包中的每个Group类型,继承对应的AbstractGroupVisitor<G> - 为
Gs中的每个Group,实现AbstractMessageVisitor中的visit方法
手动编写的示例类如下:
class MyVisitor: public AbstractMessageVisitor, public AbstractGroupVisitor<Group::A>, public AbstractGroupVisitor<Group::B> { public: void visit(GroupMessage<Group::A>& msg) final { msg.accept(static_cast<AbstractGroupVisitor<Group::A>&>(*this)); } void visit(GroupMessage<Group::B>& msg) final { msg.accept(static_cast<AbstractGroupVisitor<Group::B>&>(*this)); } };
补充说明:这么做是为了模拟虚继承的部分特性,同时避免thunks和额外虚表带来的开销。
实现方案
完全可以实现该CRTP模板,借助C++可变参数模板和继承展开特性即可完成,以下是具体实现:
// 前置声明保持与原有结构一致 enum class Group { A, B }; template <Group G> class AbstractGroupVisitor; template <Group G> class GroupMessage; class AbstractMessageVisitor; class AbstractMessage { public: virtual void accept(AbstractMessageVisitor& visitor) = 0; }; class AbstractMessageVisitor { public: virtual void visit(GroupMessage<Group::A>& msg) = 0; virtual void visit(GroupMessage<Group::B>& msg) = 0; }; template <Group G> class GroupMessage: public AbstractMessage { public: void accept(AbstractMessageVisitor& visitor) final { visitor.visit(*this); } virtual void accept(AbstractGroupVisitor<G>& visitor) = 0; }; // 核心CRTP模板实现 template <Group... Gs> class GroupsVisitor : public AbstractMessageVisitor, public AbstractGroupVisitor<Gs>... { public: // 针对每个Group的visit方法实现,用折叠表达式做合法性校验 template <Group G> void visit(GroupMessage<G>& msg) final { static_assert(((G == Gs) || ...), "当前Group未包含在Visitor的模板参数列表中"); msg.accept(static_cast<AbstractGroupVisitor<G>&>(*this)); } };
实现细节说明
- 多重继承展开:通过
public AbstractGroupVisitor<Gs>...将参数包Gs中的每个Group对应的抽象访问者类展开为基类,直接满足第二个要求。 - visit方法实现:模板化的
visit方法配合折叠表达式的静态断言,确保只有Gs中声明的Group类型会被处理,同时通过static_cast将当前对象转换为对应组的抽象访问者引用,调用GroupMessage的accept方法,完成第三个要求。 - 版本兼容性:上述实现基于C17标准(折叠表达式是C17特性),如果需要兼容C++14,可以通过递归继承或辅助模板来展开参数包实现
visit方法。
使用示例
// 先实现AbstractGroupVisitor的具体特化 template <> class AbstractGroupVisitor<Group::A> { public: virtual void processAGroupMsg(GroupMessage<Group::A>& msg) = 0; }; // 定义具体的访问者类,只需指定要处理的Group class MyVisitorImpl : public GroupsVisitor<Group::A> { public: void processAGroupMsg(GroupMessage<Group::A>& msg) override { // 这里编写Group A消息的具体处理逻辑 } };
内容的提问来源于stack exchange,提问作者Patrick Wright
相关产品推荐
相关产品推荐

