C++ 递归可变参数 Lambda

标签 c++ templates recursion lambda c++14

在 C++ 中,我们可以有一个递归可变参数模板函数。例如:

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

然而,这似乎无法使用 lambda 来完成。我主要关心的两个主要问题是:

  • 如何建立基本案例。
  • 如何使 lambda 既递归又可变。

我知道我可以使用递归 lambda,但看不到使其可变的方法。 这种类型的功能是否仅适用于更高级的语言,例如 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 if 解决了这个问题:

#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...) 失败。

constexpr if 通过在编译时进行检查来解决这个问题,甚至在检查结果为 false 时不编译 block 。在 C++17 之前,条件编译语义(不包括宏)只能使用 SFINAE 或模板特化来实现,这两者都不能仅使用 lambda 来完成。

关于C++ 递归可变参数 Lambda,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49495015/

相关文章:

c++ - 单元测试对实例变量函数的调用

c++ - lambda函数可以递归吗?

haskell - 使用守卫递归计算列表的长度抛出 "Non-exhaustive patterns in function go"

Jquery递归比较两个元素的html和属性

c++ - 为什么在打印消息时名称会打印两次?

c++ - 编译静态库时收集所有头文件

python - 如何将 constexpr 暴露给 Cython?

c++ - 使用给定类对类模板进行特化

c++ - 如果 T 是模板参数,语句 T::x(y) 有何歧义?

c++ - 如何在 C++ 中使用模板制作通用 map