是否有一个std :: function类型或类似的lambda与auto参数?

时间:2015-07-08 06:39:08

标签: c++ lambda c++14 auto generic-lambda

当我将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作为函数定义?

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

2 个答案:

答案 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不能在自身内命名。

Live example

基于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中,或者不存储在任何类型擦除的容器中。