C 递归变量lambda

C++ Recursive Variadic Lambda

本文关键字:lambda 变量 递归      更新时间:2023-10-16

在C 中,我们可以具有递归变异模板函数。例如:

template<typename ...H>
void g(){}
template<typename H, typename ...T>
void g(H h,T ...t){
    std::cout << h << "t";
    g<T...>(t...);
}

但是,使用lambdas似乎无法完成。我的两个主要问题是:

  • 如何建立基础案例。
  • 如何使lambda成为递归,同时又是variadic。

我知道我可以拥有递归的lambdas,但看不到使它变得变化的方法。这种类型的功能仅提供更高级别的语言,例如JavaScript?

编辑:到目前为止,这就是我想到的:

template<typename C,typename H, typename ...T>
std::function<void(C,H,T...)> f=[](auto&&  self,H h,T ...t){
    std::cout << h << "t";
    if(sizeof...(t)>0)
        self(self,t...);
};

这里的第一个论点将是lambda本身。但是,主要问题是,为了调用此方法,我要定义C类型,我不确定如何做(甚至可能(。

编辑:一种更简单的方法是:

auto f = [] (auto&&  self, auto&& h,auto&&... t) {
    std::cout << sizeof...(t) << "n";
    if( sizeof...(t)>0 ){
        self(self,1);
    }
};
int main()
{
    f(f,1,2,3,4,5,6);
    return 0;
}

但是给出以下错误:

main.cpp:55:13: error: use of ' [with auto:1 = &; auto:2 = int; auto:3 = {}]' before deduction of 'auto'
     self(self,1);
         ^

c 17通过constexpr解决此问题,如果:

#include <iostream>
auto f = [](auto&&... t){
    auto f_impl = [](auto& self, auto&& h, auto&&... t) {
      std::cout << sizeof...(t) << "n";
      if constexpr ( sizeof...(t)>0 ){
        self(self,t...);
      }
    };
    return f_impl(f_impl, t...);
};
int main() {
    f(1,2,3,4,5,6);
}

我还自由地将第一个参数(self(包装在"父" lambda中。

使用if(sizeof...(t))用作守护的问题是,即使您在运行时不用不正确的参数调用lambda,编译器仍然需要用sizeof...(t)==0来编译self(self, t...),而CC_4失败。

constexpr if通过在编译时进行检查来解决此问题,甚至在支票产生false时都没有编译块。在C 17之前,只能使用SFINAE或模板专业化来实现条件汇编语义(排除宏(,这两者都不能仅使用lambdas进行。

我讨厌助手,我认为使用助手是一种误导的设计。也就是说,我在不使用小帮手的情况下找不到解决方案。

这里遇到的障碍是:

  • 您不能参考其在其自己的初始化表达式中用auto声明的变量

    通过使用内部lambda将函数作为参数传递给本身来解决,以提供干净的公共接口

  • 您不能将if( sizeof... )用作"基本案例"的守卫(或任何其他"运行时"机制(,不编译

    通过"超载lambda"并实际定义所有所需的功能过载(即"基本案例"也(来解决这一点

  • 定义了lambda,您就不能"简单地"超载

    这是通过使用"小"辅助代码

  • 来解决的

总的来说,一个解决方案可以就是这样:

#include <iostream>
/////////// Helper
template<class F, class... Fs>
struct overloaded : F, overloaded<Fs...>
{
  using F::operator();
  using overloaded<Fs...>::operator();
  overloaded(F&& f, Fs&&... fs)
    : F(std::move(f))
    , overloaded<Fs...>(std::move(fs)...)
  {}
};
template<class F>
struct overloaded<F> : F
{
  using F::operator();
  overloaded(F&& f)
    : F(std::move(f))
  {}
};
template<class... Ts>
overloaded<Ts...> overload(Ts&&...lambdas)
{ return overloaded<Ts...>{std::move(lambdas)...}; }
///////// Recursive Variadic Lambda
int     main(void)
{
  auto lambda = [](auto... args)
    {
      auto lambda_impl = overload(
        [](auto self)
        {
        },
        [] (auto self, auto first, auto...rest)
        {
          std::cout << first << std::endl;
          self(self, rest...);
        });
      lambda_impl(lambda_impl, args...);
    };
  lambda(4, "lol", 8.3);
}