使用上一次调用的结果递归调用每个元组成员
Call each tuple member with a result of the previous call recursively
假设我有一个元组
std::tuple<Operation<1>, Operation<2>, Operation<3>>
。 Operation<>
有一个签名为 SomeType someFunction(SomeType)
的成员函数。我想要做的是连续调用这些操作,使得调用的结果顺序为 Operation<3>::someFunction(Operation<2>::someFunction(Operation<1>::someFunction()))
并且我将获得最终的 SomeType
值。如何使用可变参数模板(我可以访问 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 的解决方案简洁明了,但需要注意的是,您的所有操作都必须处理 return 同一类型(这是您的情况),我将尝试提出一种更广泛的方法。
这个想法是依靠重载 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
上找到一个实例
假设我有一个元组
std::tuple<Operation<1>, Operation<2>, Operation<3>>
。 Operation<>
有一个签名为 SomeType someFunction(SomeType)
的成员函数。我想要做的是连续调用这些操作,使得调用的结果顺序为 Operation<3>::someFunction(Operation<2>::someFunction(Operation<1>::someFunction()))
并且我将获得最终的 SomeType
值。如何使用可变参数模板(我可以访问 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 的解决方案简洁明了,但需要注意的是,您的所有操作都必须处理 return 同一类型(这是您的情况),我将尝试提出一种更广泛的方法。
这个想法是依靠重载 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
上找到一个实例