You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写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));
    }
};

实现细节说明

  1. 多重继承展开:通过public AbstractGroupVisitor<Gs>...将参数包Gs中的每个Group对应的抽象访问者类展开为基类,直接满足第二个要求。
  2. visit方法实现:模板化的visit方法配合折叠表达式的静态断言,确保只有Gs中声明的Group类型会被处理,同时通过static_cast将当前对象转换为对应组的抽象访问者引用,调用GroupMessage的accept方法,完成第三个要求。
  3. 版本兼容性:上述实现基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 12:00:57