以先前的通话递归致电每个元组成员

Call each tuple member with a result of the previous call recursively

本文关键字:元组 组成员 递归      更新时间:2023-10-16

,我有一个元组 std::tuple<Operation<1>, Operation<2>, Operation<3>>Operation<>具有签名SomeType someFunction(SomeType)的成员函数。我要做的是连续调用操作,以使结果的呼叫顺序为Operation<3>::someFunction(Operation<2>::someFunction(Operation<1>::someFunction())),我将获得最终的SomeType值。如何使用variadic模板(我可以访问C 17)?

我可以使用std::apply([](auto& ...x) { (..., x.someFunction()); }, tuple);调用每个成员函数,但是我需要使用上一个呼叫的输出来调用哪种表达式someFunction()

我想您可以将std::apply()结合并与lambda折叠,如下所示

   auto l = [&val](auto ... Ops) 
    { ((val = Ops.someFunc(val)), ...); };

以下是一个完整的工作示例

#include <tuple>
#include <iostream>
template <int I>
struct Oper
 {
   static constexpr int someFunc (int i)
    { return i + I; }
 };
int main ()
 {
   std::tuple<Oper<1>, Oper<2>, Oper<3>, Oper<4>>  t;
   int val {}; // starting value
   auto l = [&val](auto ... Ops) 
    { ((val = Ops.someFunc(val)), ...); };
   std::apply(l, t);
   std::cout << val << std::endl;
 }

@max66的解决方案是优雅而简洁的,但是一个警告是您所有的操作都必须处理并返回相同的类型(这是您的情况),我将尝试提出更广泛的方法。

这个想法是依靠过载的operator>>在状态和下一步上应用所需的操作。为此,让我们首先定义一些构建块:

// Just to avoid the hassle of std::forwarding by hand everywhere
#define CPPFWD(x) std::forward<decltype(x)>(x)
// We do not want to pollute the global namespace with our special operator>>
namespace combine {
// This will make the appropriate functor for each step
template <typename T, typename Op>
auto make_operation(T&& tuple_element, Op&& op) {
    return [ el = CPPFWD(tuple_element),
             op = CPPFWD(op) ](auto&& input) mutable {
        return op(el, CPPFWD(input));
    };
}
template <typename Input, typename Op>
auto operator>>(Input&& input, Op&& op) {
    return CPPFWD(op)(CPPFWD(input));
}

} // ns combine

现在我们准备解决左折实现:

template <typename State, typename Tuple, typename Op, size_t... Is>
auto fold_left_impl(State&& state, Tuple&& tuple, Op&& op, std::index_sequence<Is...>) {
    using combine::make_operation;
    // We want our operator>> to be in the immediate scope here
    // to avoid selecting an inappropriate hypothetical overload 
    using combine::operator>>;
    using std::get;
    return (CPPFWD(state) >> ... >> make_operation(get<Is>(CPPFWD(tuple)), op));
}

最后,暴露于最终用户的功能:

template <typename T>
using remove_cvref_t = std::remove_cv_t< std::remove_reference_t< T > >;
template <typename State, typename Tuple, typename Op>
auto fold_left(State&& state, Tuple&& tuple, Op&& op) {
    return fold_left_impl(
        CPPFWD(state),
        CPPFWD(tuple),
        CPPFWD(op),
        std::make_index_sequence< std::tuple_size< remove_cvref_t< Tuple > >::value > {} );
}

在您的情况下,正确的用法是:

std::tuple<Operation<1>, Operation<2>, Operation<3>> t;
fold_left(
    0,
    t,
    [](auto&& op, auto&& in) {
        return CPPFWD(op).someFunc(CPPFWD(in));
    } );

可以在Coliru

上找到一个实时示例