C++ 具有自动参数的lambda是否有std::函数类型或类似类型?
当我将lambda分配给一个显式类型的变量时(例如,当它是递归变量时,用于捕获函数本身),我使用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作为函数定义 (不要过度概括,只接受一个自动参数并硬编码
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)