代码之家  ›  专栏  ›  技术社区  ›  lrleon

用任意数量的参数生成对lambDas的调用

  •  7
  • lrleon  · 技术社区  · 7 年前

    以下定义已证明对我非常有用:

    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");
    

    将调用匿名lambda 1 我是说, 2 "hello" 是的。

    这种想法提出,我想做同样的事情,但通过拉姆达斯采取两个,三个,等等论点。例如,类似的事情

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

    有什么线索、技巧、想法等可以实现吗?

    3 回复  |  直到 6 年前
        1
  •  3
  •   bolov    7 年前

    好吧,今晚我的伏都教很强大:

    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");
    

    每个调用如何对参数进行分组是不明确的。

    所以我想把他们分组。不幸的是,我不能让它像这样为瓦拉格斯工作:

    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
  •  4
  •   max66    7 年前

    我能想到的(目前)最好的办法是用一种古老的递归方法。

    以身作则

    // 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...); }
    
        3
  •  2
  •   max66    7 年前

    一种制作 apply_on_each() 它接收一个lambda(或一个函数),该lambda(或函数)接收未定义数量的泛型参数,并调用它们(部分)以c++17的方式展开。

    老实说,这只是博洛夫的伏都教答案的概括。

    首先,一套 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); }
    

    现在 对每个应用程序() 检测函数参数个数的函数 func 并且,在BoLoV的示例之后,调用一个(第一)辅助函数,添加一个索引(double),在这个泛化的索引列表中 std::tuple 论据

    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...));
     }
    

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

    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), ...) ; }
    

    最后一个helper函数,使用索引,调用 功能 有正确的论据

    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)...); }
    

    下面是一个完整的例子

    #include <tuple>
    #include <utility>
    #include <iostream>
    #include <type_traits>
    
    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); }
    
    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)...); }
    
    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), ...) ; }
    
    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...));
     }
    
    int main()
     {
       auto l1 = [](auto a)
        { std::cout << "- l1:" << a << std::endl; };
    
       auto l2 = [](auto a, auto b)
        { std::cout << "- l2:" << a << ", " << b << std::endl; };
    
       auto l3 = [](auto a, auto b, auto c)
        { std::cout << "- l3:" << a << ", " << b << ", " << c << std::endl; };
    
       apply_on_each(l1, 1, 2l, 3ll, "4", '5', 6.0);
       apply_on_each(l2, 1, 2l, 3ll, "4", '5', 6.0);
       apply_on_each(l3, 1, 2l, 3ll, "4", '5', 6.0);
     }