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

如何用折叠表达式替代分支实现双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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 16:05:11