如何用折叠表达式替代分支实现双std::any参数访问器
用折叠表达式消除双std::any参数的if-else分支
我正在尝试用折叠表达式(或其他元编程技术)消除以下代码片段中的if-else分支:
template<typename ... Ts> struct VisitorAny { template<typename Callable> auto apply_visitor(Callable&& callable, const std::any& data1, const std::any& data2) { using first_type = std::tuple_element_t<0, std::tuple<Ts...>>; using R = decltype(std::declval<Callable>()(std::declval<first_type>(), std::declval<first_type>())); R ret{}; if (data1.type() == typeid(int) && data2.type() == typeid(int)) { ret = std::forward<Callable>(callable)(std::any_cast<int>(data1), std::any_cast<int>(data2)); } else if (data1.type() == typeid(float) && data2.type() == typeid(int)) { ret = std::forward<Callable>(callable)(std::any_cast<float>(data1), std::any_cast<int>(data2)); } else if (data1.type() == typeid(int) && data2.type() == typeid(float)) { ret = std::forward<Callable>(callable)(std::any_cast<int>(data1), std::any_cast<float>(data2)); } else if (data1.type() == typeid(float) && data2.type() == typeid(float)) { ret = std::forward<Callable>(callable)(std::any_cast<float>(data1), std::any_cast<float>(data2)); } return ret; } };
对于仅接收单个std::any实例的可调用对象,我已经实现了简洁的版本:
template<typename ... Ts> struct VisitorAny { template<typename Tany, typename Tr, typename Callable> void visit(const Tany& any_inp, Tr& ret, Callable&& callable) { ret = std::forward<Callable>(callable)(any_inp); }; template<typename Callable> auto apply_visitor(Callable&& callable, const std::any& data) { using first_type = std::tuple_element_t<0, std::tuple<Ts...>>; using R = decltype(std::declval<Callable>()(std::declval<first_type>())); R ret{}; (((data.type() == typeid(Ts)) ? ((visit(std::any_cast<Ts>(data), ret, callable)), false) : true) && ...); return ret; }; };
现在我想了解如何将类似技术应用于接收两个std::any实例的可调用对象,且不能使用std::visit和std::variant。
解决方案
核心思路是通过双重折叠表达式生成所有模板参数类型的两两组合,遍历这些组合来匹配两个std::any的实际类型,从而消除硬编码的if-else分支。
完整实现代码
template<typename ... Ts> struct VisitorAny { // 辅助函数:处理单个类型组合的匹配与调用 template<typename T1, typename T2, typename Tr, typename Callable> void visit_pair(const std::any& data1, const std::any& data2, Tr& ret, Callable&& callable) { if (data1.type() == typeid(T1) && data2.type() == typeid(T2)) { ret = std::forward<Callable>(callable)(std::any_cast<T1>(data1), std::any_cast<T2>(data2)); } } // 双std::any参数的apply_visitor实现 template<typename Callable> auto apply_visitor(Callable&& callable, const std::any& data1, const std::any& data2) { // 推导返回类型:以第一个类型组合作为基准 using first_type = std::tuple_element_t<0, std::tuple<Ts...>>; using R = decltype(std::declval<Callable>()(std::declval<first_type>(), std::declval<first_type>())); R ret{}; // 双重折叠表达式:遍历所有Ts的两两类型组合 (((visit_pair<Ts, Ts>(data1, data2, ret, std::forward<Callable>(callable))), ...), ...); return ret; } // 单个std::any参数的实现保持不变 template<typename Tany, typename Tr, typename Callable> void visit(const Tany& any_inp, Tr& ret, Callable&& callable) { ret = std::forward<Callable>(callable)(any_inp); }; template<typename Callable> auto apply_visitor(Callable&& callable, const std::any& data) { using first_type = std::tuple_element_t<0, std::tuple<Ts...>>; using R = decltype(std::declval<Callable>()(std::declval<first_type>())); R ret{}; (((data.type() == typeid(Ts)) ? ((visit(std::any_cast<Ts>(data), ret, callable)), false) : true) && ...); return ret; }; };
关键说明
- 双重折叠表达式:
(((visit_pair<Ts, Ts>(...)), ...), ...)会展开为所有模板参数Ts的两两组合调用。例如当Ts为int, float时,会自动生成visit_pair<int,int>、visit_pair<int,float>、visit_pair<float,int>、visit_pair<float,float>这四个调用,完全覆盖原代码中的四个if-else分支。 - 类型匹配逻辑:辅助函数
visit_pair负责检查当前类型组合是否与两个std::any的实际类型匹配,匹配成功则调用可调用对象并赋值给返回值。 - 返回类型推导:依旧以第一个模板类型的组合作为基准推导返回值类型,确保和原代码行为一致。
如果需要处理未匹配到任何类型组合的情况,可以在函数末尾添加断言或错误处理逻辑,原代码中默认返回默认构造的R,这里保持一致。
内容的提问来源于stack exchange,提问作者user3786219
相关产品推荐
相关产品推荐

