C++ Visitor Pattern实现不同Visitor返回不同类型的解决方案咨询
解决方案
C++确实不允许虚函数声明为模板,我们可以用以下几种常用方案绕过这个限制:
方案1:返回值存储在Visitor实例中(兼容性最好,全C++版本支持)
不需要修改IVisitable的虚函数签名,将返回值存储在Visitor自身的成员变量里,accept统一返回void,调用完成后直接从Visitor实例提取结果即可。
代码示例:
// 非模板Visitor基类 class IVisitor { public: virtual ~IVisitor() = default; virtual void visit(IVisitable& visitable) = 0; }; class IVisitable { public: virtual ~IVisitable() = default; virtual void accept(IVisitor& visitor) = 0; }; // 模板派生Visitor层,封装对应类型的返回值存储逻辑 template<typename R> class ResultVisitor : public IVisitor { protected: R result_; public: R get_result() const { return result_; } }; // 返回int的具体Visitor class ConcreteVisitorOne : public ResultVisitor<int> { public: void visit(IVisitable& visitable) override { // 你的业务计算逻辑 result_ = 123; } }; // 返回bool的具体Visitor class ConcreteVisitorTwo : public ResultVisitor<bool> { public: void visit(IVisitable& visitable) override { // 你的业务计算逻辑 result_ = true; } }; class ConcreteVisitable : public IVisitable { public: void accept(IVisitor& visitor) override { visitor.visit(*this); } }; // 调用示例 int main() { ConcreteVisitable obj; ConcreteVisitorOne vis1; obj.accept(vis1); int res1 = vis1.get_result(); // 获取int类型返回值 ConcreteVisitorTwo vis2; obj.accept(vis2); bool res2 = vis2.get_result(); // 获取bool类型返回值 return 0; }
方案2:类型擦除统一返回值(C++17及以上版本支持)
如果不想额外维护Visitor的成员变量,可以用std::any做类型擦除,统一accept的返回值类型为std::any,调用端再做对应类型转换即可:
代码示例:
#include <any> class IVisitor { public: virtual ~IVisitor() = default; virtual std::any visit(IVisitable& visitable) = 0; }; class IVisitable { public: virtual ~IVisitable() = default; virtual std::any accept(IVisitor& visitor) = 0; }; class ConcreteVisitorOne : public IVisitor { public: std::any visit(IVisitable& visitable) override { return 123; // 返回int } }; class ConcreteVisitorTwo : public IVisitor { public: std::any visit(IVisitable& visitable) override { return true; // 返回bool } }; class ConcreteVisitable : public IVisitable { public: std::any accept(IVisitor& visitor) override { return visitor.visit(*this); } }; // 调用示例 int main() { ConcreteVisitable obj; ConcreteVisitorOne vis1; int res1 = std::any_cast<int>(obj.accept(vis1)); ConcreteVisitorTwo vis2; bool res2 = std::any_cast<bool>(obj.accept(vis2)); return 0; }
如果返回类型是提前已知的有限集合,可以用std::variant代替std::any,性能更好且不会出现类型转换失败的未定义行为。
方案3:静态访问者模式(无运行时虚函数开销,适合可访问类型固定的场景)
如果你的所有ConcreteVisitable类型是编译期可枚举的,可以直接用std::variant存储所有可访问类型,配合std::visit实现静态调度,完全不需要虚函数基类,天然支持任意返回类型:
#include <variant> // 不需要定义IVisitable、IVisitor虚基类 class ConcreteVisitableA {}; class ConcreteVisitableB {}; using VisitableVariant = std::variant<ConcreteVisitableA*, ConcreteVisitableB*>; // 返回int的Visitor class VisitorReturnInt { public: int operator()(ConcreteVisitableA* a) { return 1; } int operator()(ConcreteVisitableB* b) { return 2; } }; // 返回bool的Visitor class VisitorReturnBool { public: bool operator()(ConcreteVisitableA* a) { return true; } bool operator()(ConcreteVisitableB* b) { return false; } }; // 调用示例 int main() { VisitableVariant obj = new ConcreteVisitableA(); int res1 = std::visit(VisitorReturnInt{}, obj); bool res2 = std::visit(VisitorReturnBool{}, obj); return 0; }
内容的提问来源于stack exchange,提问作者user14749247
相关产品推荐
相关产品推荐

