如何在C++中使用访问者模式且无需编写大量重载
访问者模式下实现同类型扩展操作的优化方案
问题背景
定义了DragonVector<T>和UnbiasedDragon<T>两种数据类型,基于访问者模式实现动态类型推断,要求仅允许同类型实例之间执行扩展操作。最初尝试使用auto参数编写兜底函数时,在C++17环境下触发'auto' not allowed in function prototype错误;穷举所有类型组合的重载方式,在类数量增多后扩展性极差;而简单的模板兜底方案也因重载优先级问题无法正常工作。需要找到无需穷举所有组合的实现方式。
可行解决方案
方案1:SFINAE约束兜底模板(兼容C++17)
利用std::enable_if和类型判断,让精确匹配的重载优先生效,兜底模板仅在类型不合法或不匹配时触发。
#include <type_traits> #include <iostream> // 前置定义目标类型 template<class T> class DragonVector { public: void extend(const DragonVector<T>&) { /* 扩展逻辑实现 */ } }; template<class T> class UnbiasedDragon { public: void extend(const UnbiasedDragon<T>&) { /* 扩展逻辑实现 */ } }; template<class T> class ExtendVisitor{ public: // 精确匹配同类型DragonVector void operator()(DragonVector<T>& vec1, const DragonVector<T>& vec2){ vec1.extend(vec2); } // 精确匹配同类型UnbiasedDragon void operator()(UnbiasedDragon<T>& vec1, const UnbiasedDragon<T>& vec2){ vec1.extend(vec2); } // 兜底模板:仅当类型不属于目标类型,或两个参数类型不一致时生效 template<class TT, class U> std::enable_if_t< !std::is_same_v<TT, DragonVector<T>> && !std::is_same_v<TT, UnbiasedDragon<T>> || !std::is_same_v<TT, U> > operator()(TT&, const U&){ std::cout<<"wrong class"<<std::endl; } };
说明:通过std::enable_if_t的条件判断,确保兜底模板不会和精确匹配的重载产生二义性,新增目标类型时仅需添加对应精确重载即可。
方案2:C++20概念约束(更简洁高效)
如果可以升级到C++20,使用概念直接约束允许的类型,实现通用同类型处理:
#include <concepts> #include <iostream> // 前置定义目标类型 template<class T> class DragonVector { public: void extend(const DragonVector<T>&) { /* 扩展逻辑实现 */ } }; template<class T> class UnbiasedDragon { public: void extend(const UnbiasedDragon<T>&) { /* 扩展逻辑实现 */ } }; // 定义概念:限定为允许执行扩展操作的类型 template<class Derived, class T> concept DragonLike = std::same_as<Derived, DragonVector<T>> || std::same_as<Derived, UnbiasedDragon<T>>; template<class T> class ExtendVisitor{ public: // 通用处理:所有满足DragonLike的同类型实例 template<DragonLike<T> Vec> void operator()(Vec& vec1, const Vec& vec2){ vec1.extend(vec2); } // 兜底处理:不满足同类型或不属于DragonLike的情况 template<class TT, class U> void operator()(TT&, const U&){ std::cout<<"wrong class"<<std::endl; } };
说明:新增目标类型时,只需修改DragonLike概念的条件,无需编写新的重载函数,扩展性极强。
方案3:标签分发+类型判断
给所有允许的类型统一添加标签基类,通过编译期类型判断实现分发:
#include <iostream> #include <type_traits> // 定义统一标签基类 struct DragonTypeTag {}; // 目标类型继承标签 template<class T> class DragonVector : public DragonTypeTag { public: void extend(const DragonVector<T>&) { /* 扩展逻辑实现 */ } }; template<class T> class UnbiasedDragon : public DragonTypeTag { public: void extend(const UnbiasedDragon<T>&) { /* 扩展逻辑实现 */ } }; template<class T> class ExtendVisitor{ private: // 处理合法同类型情况 template<class Vec> std::enable_if_t<std::is_base_of_v<DragonTypeTag, Vec>> handle(Vec& vec1, const Vec& vec2) { vec1.extend(vec2); } // 处理非法情况 template<class TT, class U> void handle(TT&, const U&) { std::cout<<"wrong class"<<std::endl; } public: template<class TT, class U> void operator()(TT& vec1, const U& vec2) { // 先判断是否为同类型,再分发处理 if constexpr (std::is_same_v<TT, U>) { handle(vec1, vec2); } else { std::cout<<"wrong class"<<std::endl; } } };
说明:新增目标类型时,只需让类继承DragonTypeTag并实现extend方法,无需修改ExtendVisitor的代码,完全解耦类型添加和访问者逻辑。
内容的提问来源于stack exchange,提问作者shubh gupta
相关产品推荐
相关产品推荐

