当我将lambda分配给显式类型的变量时(例如,当它是递归的时,为了捕获函数本身),我使用std::function
。
考虑这个愚蠢的"比特计数"作为一个例子:
std::function<int(int)> f;
f = [&f](int x){ return x ? f(x/2)+1 : 0; };
当我们使用auto参数来推广x
时,如C ++ 14泛型lambda中所介绍的情况怎么样?
std::function<int(???)> f;
f = [&f](auto x){ return x ? f(x/2)+1 : 0; };
显然,我无法在auto
类型参数中放置function
。
是否有可能通常定义一个仿函数类来覆盖上面的确切情况,但仍然使用lambda作为函数定义?
(不要过度概括这一点,只接受一个自动参数并对返回值进行硬编码。)用例适用于上述场景:通过引用为递归调用捕获函数本身
答案 0 :(得分:4)
您可以创建一个lambda,通过将其作为参数传递给自身来调用自身:
auto f = [](auto self, auto x) -> int {
return x ? self(self, x / 2) + 1 : 0;
};
std::cout << f(f, 10);
然后你可以在另一个lambda中捕获lambda,所以你不必担心将它传递给自己:
auto f2 = [&f](auto x) {
return f(f, x);
};
std::cout << f2(10);
答案 1 :(得分:3)
这是一个基于y-combinator的快速递归引擎:
template<class F>
struct recursive_t {
F f;
// note Self must be an lvalue reference. Things get
// strange if it is an rvalue:
// invoke makes recursive ADL work a touch better.
template<class Self, class...Args>
friend auto invoke( Self& self, Args&&...args )
-> decltype( self.f( self, std::declval<Args>()... ) )
{
return self.f( self, std::forward<Args>(args)... );
}
// calculate return type using `invoke` above:
template<class Self, class...Args>
using R = decltype( invoke( std::declval<Self>(), std::declval<Args>()... ) );
template<class...Args>
R<recursive_t&, Args...> operator()(Args&&...args)
{
return invoke( *this, std::forward<Args>(args)... );
}
template<class...Args>
R<recursive_t const&, Args...> operator()(Args&&...args)const
{
return invoke( *this, std::forward<Args>(args)... );
}
};
template<class F>
recursive_t< std::decay_t<F> > recurse( F&& f )
{
return {std::forward<F>(f)};
}
现在你可以这样做:
auto f = recurse( [](auto&& f, auto x){ return x ? f(x/2)+1 : 0; } );
你得到一个没有&
捕获的递归lambda(它将其使用限制在当前范围内)。
通过引用捕获std::function
意味着lambda的生命周期是当前作用域,并且每个递归调用都需要重新进行类型擦除(阻止任何可能的优化,如尾递归,在递归调用上) 。其他类似的解决方案也是如此。
需要使用recursive_t
而不是使用lambda,因为lambda不能在自身内命名。
基于lambda的版本在实现上稍微简单一些。请注意,对于可变和不可变的lambda,您需要不同的类型函数:
template<class F>
auto recurse( F&& f ) {
return [f=std::forward<F>(f)](auto&&...args){
return f(f, decltype(args)(args)...);
};
};
recursive_t
的作用类似于:
auto fib = recurse( [](auto&& fib, int x){ if (x<2) return 1; return fib(x-1)+fib(x-2); } );
lambda版本的工作方式如下:
auto fib = recurse( [](auto&& self, int x){ if (x<2) return 1; return self(self, x-1)+self(self,x-2); } );
我个人觉得更尴尬。
描述recurse
的类型也更难。对于recursive_t
版本,recurse
的类型为:
((A->B)->A->B)->(A->B)
这很尴尬,但是有限型。
lambda版本比较棘手。 recursive
的函数参数类型为:
F:= F->A->B
这是令人讨厌的无限,然后recurse
的类型为
F->A->(A->B)
继承了无限。
无论如何,recurse
返回值可以存储在普通的std::function
中,或者不存储在任何类型擦除的容器中。