在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似乎无法做到这一点。我的两个主要关注点是:
我知道我可以有递归的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);
^
答案 0 :(得分:5)
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
)包装在&#34;父母&#34;拉姆达。
使用if(sizeof...(t))
作为保护的问题是,即使您不在运行时使用不正确的参数调用lambda,编译器仍需要使用{{1}编译表达式self(self, t...)
},失败了。
sizeof...(t)==0
通过在编译时进行此检查来解决此问题,甚至在检查产生constexpr if
时甚至不编译块。在C ++ 17之前,条件编译语义(不包括宏)只能使用SFINAE或模板专业化来实现,这两者都不能仅使用lambdas来完成。
答案 1 :(得分:2)
我讨厌帮手,我认为使用帮手是一种误导性的设计。也就是说,如果不使用小帮手,我无法找到解决方案。
这里遇到的障碍如下:
您不能在其自己的初始化表达式中引用用auto
声明的变量
这可以通过使用内部lambda将函数作为参数传递给自身来解决,以提供干净的公共接口
您不能将if( sizeof... )
用作&#34; base-case&#34;的保护(或任何其他&#34;运行时&#34;机制)。因为你仍然会调用一个函数,该函数不能接受那个if
块中没有编译的参数数量
这是通过&#34;重载lambda&#34;来解决的。并实际定义了所有需要的函数重载(即&#34;基本情况&#34;也)
你不能简单地&#34;&#34;一旦定义了lambda就会重载
这是通过使用&#34; small&#34;帮助代码
总而言之,一个解决方案可能就是这样:
#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);
}