C++ 具有自动参数的lambda是否有std::函数类型或类似类型?

C++ 具有自动参数的lambda是否有std::函数类型或类似类型?,c++,lambda,c++14,auto,generic-lambda,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放在函数类型参数中 是否有可能定义一个足够通用的函子类,以涵盖上述确切情况,但仍然使用lambda作为函数定义 (不要过度概括,只接受一个自动参数并硬编码

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

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

std::function<int(int)> f;
f = [&f](int x){ return x ? f(x/2)+1 : 0; };
显然,我不能将
auto
放在
函数
类型参数中

是否有可能定义一个足够通用的函子类,以涵盖上述确切情况,但仍然使用lambda作为函数定义


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

您可以创建一个lambda,通过将其作为参数传递给自身来调用自身:

auto f = [](auto self, auto x) -> int {
    return x ? self(self, x / 2) + 1 : 0;
};

std::cout << f(f, 10);
auto f=[](auto-self,auto-x)->int{
返回x?self(self,x/2)+1:0;
};

std::cout这是一个基于y组合器的快速递归引擎:

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)};
}
您会得到一个递归lambda,它没有
&
捕获(这将其使用限制在当前范围内)

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

需要使用
recursive\u 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)...);
  };
};
lambda版本的工作原理如下:

auto fib = recurse( [](auto&& fib, int x){ if (x<2) return 1; return fib(x-1)+fib(x-2); } );
auto fib = recurse( [](auto&& self, int x){ if (x<2) return 1; return self(self, x-1)+self(self,x-2); } );
这很尴尬,但却是一种有限类型

lambda版本更为复杂。递归函数参数的类型为:

((A->B)->A->B)->(A->B)
F:= F->A->B
这是令人烦恼的无限,然后
递归

F->A->(A->B)
它继承了无限


无论如何,
recurse
返回值可以存储在一个普通的
std::function
中,也可以不存储在任何类型的擦除容器中。

我认为,这里的问题是lamba函数需要一个确定的类型-
auto
只是让编译自行完成。这并不意味着您可以将lambda函数传递给任何具有任何类型的代码-需要在lambda函数的编译时知道该类型?这样行吗?(可能只是
auto
而不是
auto&
)我手边没有一个支持C++14的编译器,所以我不能轻易测试它。
std::function
使用类型擦除。我相当肯定,从根本上讲,编写一个接受任意类型的类型擦除函数对象包装器是不可能的,因为每一组参数类型都需要创建一个新函数,这只有在编译器能够看到包装的函数模板时才能完成。解决方法包括使用一组固定的参数类型集(使用现有的重载集/在类型擦除之前预计算所有重载),或类型擦除参数。@dyp您可以在参数类型或函数上创建类型擦除,但在这两种类型上都创建类型擦除很难反映您的函数(如boost phoenix)转换为数据(基本上是在参数类型上重新编译函数)。举个具体的例子,想象一下将gcc与二进制文件一起发送,将函数存储为字符串。当传递参数时,您可以确定它们的类型,打印该类型的函数,编译动态库,加载它,然后运行该函数。不是完全不可能。并且要求函数定义为除了简单C++代码之外的其他东西。如果您使用OpenCL之类的东西,那么内核就是在运行时编译的字符串。您可以在运行时使用“类型信息”输入操作上述内核,生成无限多个可能的内核实现中的一个,编译它,然后调用它。当然,除了真正简单的例子之外,安全地执行这个操作是非常困难的,这是C++风格模板元编程优于基于字符串的元编程的优点之一。虽然你没有回答实际问题(我想为函数子键入一个类型名),但我想我可以用它来解决我的实际问题。(这太长了,无法解释-我想我需要显式地给函子一个类型,但我想我可以在我的具体场景中用你的解决方案解决这个问题)。
((A->B)->A->B)->(A->B)
F:= F->A->B
F->A->(A->B)