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

C++实现tuple元素apply方法的函数复合式链式调用

问题说明
  • 现有一个元素类型为T1,...,TN的std::tuple,元组中每个类型都实现了apply()方法。
  • 需要实现一个函数,接收该元组和一个初始值作为入参,返回从初始值开始链式调用所有元素apply()方法的结果。

待补全的代码框架和测试用例如下:

template <typename... Args, typename Input>
auto apply(std::tuple<Args...> const &tpl, Input x) {
  // return ???
}

// 测试用例定义
struct Sqr {
  static int apply(int x) { return x * x; }
};

enum class Choice {
  One,
  Two,
};

struct Choose {
  static int apply(Choice choice) {
    switch (choice) {
    case Choice::One:
      return 1;
    case Choice::Two:
      return 2;
    }
  }
};

void test() {
  auto tpl = std::tuple(Sqr{}, Choose{});
  assert(apply(tpl, Choice::One) == 1);
  assert(apply(tpl, Choice::Two) == 4);
}

之前尝试用折叠表达式、遍历tuple元素调用函数的方案都无法编译,核心问题是:需要将上一次apply()的返回值作为下一次调用的入参,而非所有调用都传入初始值。
错误实现示例,问题在于所有apply()调用都接收同一个初始值x,中间结果无法传递:

template <typename... Args, typename Input>
auto apply(std::tuple<Args...> const &tpl, Input x) {
  return std::apply([&x](auto &&... args) {
    return (..., args.apply(x));
  }, tpl);
}

规则约束

  • 调用顺序从元组最后一个元素向前执行,和数学函数复合规则一致,即(f * g)(x) := f(g(x))
  • 不强制约束元组内各元素apply()方法的输入输出类型,仅要求相邻调用的输入输出类型可匹配即可。

实现方案

C++17 兼容版本

通过std::index_sequence倒序遍历tuple元素,配合逗号折叠表达式顺序执行调用,每次将上一次的返回值作为下一次的入参:

namespace detail {
template <std::size_t... Is, typename Tuple, typename Input>
auto apply_reverse_impl(std::index_sequence<Is...>, Tuple const& tpl, Input x) {
    constexpr auto tpl_size = sizeof...(Is);
    auto val = std::move(x);
    // 逗号左折叠:按Is从小到大顺序执行,对应tuple索引从大到小
    ( (val = std::get<tpl_size - 1 - Is>(tpl).apply(std::move(val))), ... );
    return val;
}
}

template <typename... Args, typename Input>
auto apply(std::tuple<Args...> const &tpl, Input x) {
    return detail::apply_reverse_impl(
        std::index_sequence_for<Args...>{},
        tpl,
        std::move(x)
    );
}

逻辑说明

  1. std::index_sequence_for<Args...>生成从0到N-1的索引序列(N为tuple元素个数)
  2. 遍历索引时,通过tpl_size -1 - Is将正序索引转换为倒序,保证第一个执行的是tuple最后一个元素的apply()
  3. 逗号折叠表达式严格按从左到右顺序执行,每次把前一次的返回值移动传入下一次调用,避免不必要的拷贝
  4. 相邻调用类型只要可匹配即可正常编译,不需要统一所有apply()的输入输出类型

C++20 简化版本

可以直接在lambda内使用模板参数,省略单独的辅助函数:

template <typename... Args, typename Input>
auto apply(std::tuple<Args...> const &tpl, Input x) {
    return std::apply([](auto const&... fns, auto val) {
        constexpr auto tpl_size = sizeof...(fns);
        return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
            auto res = std::move(val);
            auto tpl_ref = std::tie(fns...);
            ( (res = std::get<tpl_size - 1 - Is>(tpl_ref).apply(std::move(res))), ... );
            return res;
        }(std::index_sequence_for<Args...>{});
    }, tpl, std::move(x));
}

内容的提问来源于stack exchange,提问作者ransh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 11:27:22