生成具有任意数量参数的lambda调用

时间:2018-06-23 18:49:21

标签: c++17 variadic-templates template-meta-programming generic-lambda fold-expression

事实证明,以下定义对我非常有用:

template<class Func, class... Args>
void apply_on_each_args(Func f, Args... args)
{
    (f(args), ...);
}

基本上,参数包折叠在逗号运算符上,允许定义对带有参数的函数的多次调用。例如:

apply_on_each_args([] (auto x) { cout << x << endl; }, 1, 2, "hello");

将在12"hello"上调用匿名lambda。

这个想法提出了,我想做同样的事情,只是传递带有两个,三个等参数的lambda。例如,类似的东西

apply_on_each_args([] (auto x, auto y) { /* use x and y */ }, 1, 2, "hello",  "bye");

任何可以实现目标的线索,技术,思想等?

3 个答案:

答案 0 :(得分:4)

(目前)我能想象的最好的方法是旧的递归方法。

通过示例

// ground case
template <typename Func>
void apply_on_each_2_args (Func)
 { }

// recursive case
template <typename Func, typename A0, typename A1, typename ... Args>
void apply_on_each_2_args (Func f, A0 a0, A1 a1, Args ... args)
 { f(a0, a1); apply_on_each_2_args(f, args...); }

答案 1 :(得分:3)

好吧,我的巫毒今晚很强:

auto foo(int, int) -> void;

template <class Func, class... Args, std::size_t... I>
void apply_on_2x_indexes(Func f,  std::index_sequence<I...>, std::tuple<Args...> t)
{
    (f(std::get<I * 2>(t), std::get<I * 2 + 1>(t)), ...);
}

template<class Func, class... Args>
void apply_on_each_2_args(Func f, Args... args)
{
    apply_on_2x_indexes(f, std::make_index_sequence<sizeof...(Args) / 2>{},
                        std::tuple{args...});   
}

auto test()
{
    apply_on_each_2_args(foo, 1, 2, 3, 4); // calls foo(1, 2) foo(3, 4)
}

为简洁起见,忽略转发。

为了更好地了解其工作原理,我们可以手动展开:

apply(on_each_2_args(foo, 1, 2, 3, 4))
↳ apply_on_2x_indexes(f, std::index_sequence<0, 1>{}, std::tuple{1, 2, 3, 4})
  ↳ (f(std::get<0 * 2>(t), std::get<0 * 2 + 1>(t)),  f(std::get<1 * 2>(t), std::get<1 * 2 + 1>(t)))
    (f(std::get<0>(t), std::get<1>(t)),  f(std::get<2>(t), std::get<3>(t)))
    (f(1, 2), f(3, 4))

另一种方法:

我不喜欢您的通话语法的一件事

apply_on_each_2_args([] (auto x, auto y) { }, 1, 2, "hello",  "bye");

目前尚不清楚每次调用如何对参数进行分组。

所以我想将它们分组。不幸的是,我无法让它像这样对varargs起作用:

apply_on_each_2_args([] (auto x, auto y) { }, {1, 2}, {"hello",  "bye"});

但我们可以更详细地介绍tuple

template<class Func, class... Args>
void apply_on_each_2_args(Func f, Args... args)
{
    (std::apply(f, args), ...);
}

auto test()
{
    apply_on_each_2_args([](auto a, auto b){ /*use a, b*/ },
                         std::tuple{1, 2}, std::tuple{"hello", "bye"});
}

并不是您所要问的完全正确,而是一种值得考虑的方法。

答案 2 :(得分:2)

一种制作Number(t[0]), it returns NaN.的方法,该方法接收一个lambda(或一个函数),该lambda(或一个函数)接收不确定数量的通用参数,并以C ++ 17的方式调用它们(部分)展开。

说实话,这只是Bolov伏都教答案的概括。

首先,一组apply_on_each()函数检测一个函数的参数个数(假设参数是通用的,因此假定整数零的列表是可以接受的)

constexpr

现在,template <typename F, typename ... Ts> constexpr auto numArgsH (int, Ts ... ts) -> decltype( std::declval<F>()(ts...), std::size_t{} ) { return sizeof...(Ts); } template <typename F, typename ... Ts> constexpr auto numArgsH (long, Ts ... ts) { return numArgsH<F>(0, 0, ts...); } template <typename F> constexpr auto numArgs () { return numArgsH<F>(0); } 函数可以检测函数apply_on_each()的参数数量,并按照Bolov的示例,调用(第一个)辅助函数,添加一个(在此概括中为double)索引和参数的func

std::tuple

现在,第一个帮助函数使用C ++ 17折叠“解压缩”第一个索引序列,并调用第二个帮助函数

template <typename F, typename ... Ts>
void apply_on_each (F func, Ts ... ts)
 {
   static constexpr auto num_args { numArgs<F>() };

   apply_on_each_h1(func,
                    std::make_index_sequence<sizeof...(Ts)/num_args>{},
                    std::make_index_sequence<num_args>{},
                    std::make_tuple(ts...));
 }

现在是最后一个帮助程序函数,该函数在处理索引时使用正确的参数调用template <typename F, std::size_t ... Is, std::size_t ... Js, typename ... Ts> void apply_on_each_h1 (F func, std::index_sequence<Is...> const &, std::index_sequence<Js...> const & js, std::tuple<Ts...> const & t) { (apply_on_each_h2<Is>(func, js, t), ...) ; }

func

以下是完整示例

template <std::size_t I, typename F, std::size_t ... Js, typename ... Ts>
void apply_on_each_h2 (F func,
                       std::index_sequence<Js...> const & js, 
                       std::tuple<Ts...> const & t)
 { func(std::get<I*sizeof...(Js)+Js>(t)...); }