6

当我将 lambda 分配给显式类型的变量时(例如,当它是递归时,以捕获函数本身),我使用std::function.

以这个愚蠢的“位计数”函数为例:

std::function<int(int)> f;
f = [&f](int x){ return x ? f(x/2)+1 : 0; };

x如 C++14 泛型 lambda 中介绍的那样,当我们使用 auto 参数进行泛化时,情况如何?

std::function<int(???)> f;
f = [&f](auto x){ return x ? f(x/2)+1 : 0; };

显然,我不能放入auto类型function参数。

是否有可能定义一个足以涵盖上述确切情况的仿函数类,但仍使用 lambda 进行函数定义?

(不要过度概括,只接受一个自动参数并对返回值进行硬编码。)用例将适用于上述场景:通过引用捕获函数本身以进行递归调用。

4

2 回答 2

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);
于 2015-07-08T07:39:45.440 回答
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(这将其使用限制在当前范围内)。

通过引用捕获 astd::function意味着您的 lambda 的生命周期是当前范围,并且每个递归调用都需要遍历类型擦除(阻止递归调用上的任何可能的优化,例如尾递归)。其他类似的解决方案也是如此。

需要使用 ofrecursive_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,或者不存储在任何类型擦除的容器中。

于 2015-07-08T15:12:42.327 回答