如何使用模板将 lambda 转换为 std: : 函数

基本上,我希望能够做的就是获取一个具有任意数量的任意类型参数的 lambda,并将其转换为一个 std: : 函数。 我尝试了以下方法,但两种方法都不管用。

std::function([](){});//Complains that std::function is missing template parameters
template <typename T> void foo(function<T> f){}
foo([](){});//Complains that it cannot find a matching candidate

然而,下面的代码确实可以工作,但是它不是我想要的,因为它需要显式地声明模板参数,而这些参数不适用于泛型代码。

std::function<void()>([](){});

我整个晚上都在研究函数和模板但我就是搞不明白,所以如果有任何帮助我会很感激的。

正如注释中提到的,我之所以尝试这样做是因为我试图在 C + + 中使用可变模板实现局部套用。不幸的是,这在使用 lambdas 时严重失败。例如,我可以使用函数指针传递一个标准函数。

template <typename R, typename...A>
void foo(R (*f)(A...)) {}
void bar() {}
int main() {
foo(bar);
}

但是我不知道怎么把 lambda 传给这样的可变参数函数。我之所以对将通用 lambda 转换为 std: : function 感兴趣,是因为我可以执行以下操作,但最终需要将模板参数显式地声明给 std: : function,这正是我试图避免的。

template <typename R, typename...A>
void foo(std::function<R(A...)>) {}
int main() {
foo(std::function<void()>([](){}));
}
37441 次浏览

不是通过 std::bind实现了对 已经的局部套用吗?

auto sum = [](int a, int b){ return a+b; };
auto inc = std::bind( sum, _1, 1 );
assert( inc(1)==2 );

如果不显式指定模板参数 T,就不能将 lambda 函数对象作为 std::function<T>类型的参数传递。模板类型推理试图匹配的类型,您的 lambda 函数的 std::function<T>,它只是不能在这种情况下-这些类型是不一样的。模板类型推导不考虑类型之间的转换。

如果您可以给它一些其他的方法来推断类型,那么它是可能的。您可以通过将函数参数封装在 identity类型中来实现这一点,这样在尝试将 lambda 与 std::function匹配时(因为依赖类型只是被类型推导忽略) ,函数参数不会失败,并给出一些其他参数。

template <typename T>
struct identity
{
typedef T type;
};


template <typename... T>
void func(typename identity<std::function<void(T...)>>::type f, T... values) {
f(values...);
}


int main() {
func([](int x, int y, int z) { std::cout << (x*y*z) << std::endl; }, 3, 6, 8);
return 0;
}

但是这在您的情况下显然没有用处,因为您不想在以后传递这些值。

因为您不希望指定模板参数,也不希望传递其他可以推导出模板参数的参数,所以编译器将无法推导出 std::function参数的类型。

推断 lambda 的调用签名或“ make _ function”的任意调用所示,您可以从 lambda 的(单个) operator()推断它的调用签名(或任何其他具有单个调用签名的函数) :

template<typename T> struct remove_class { };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...)> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };


template<typename T>
struct get_signature_impl { using type = typename remove_class<
decltype(&std::remove_reference<T>::type::operator())>::type; };
template<typename R, typename... A>
struct get_signature_impl<R(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(&)(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(*)(A...)> { using type = R(A...); };
template<typename T> using get_signature = typename get_signature_impl<T>::type;

但是,这是一种相当不灵活的方法; 正如 R。 Martinho Fernandes 所说,它不适用于具有多个 operator()的函数,也不适用于具有 模板 operator()或(C + + 14)多态 lambdas 的函数。这就是为什么 bind将其结果类型的推断推迟到最终的调用尝试。

可以使用派生、 dectype、可变模板和一些类型特性来获得 lambda 所需的 std: : function type:

namespace ambient {


template <typename Function>
struct function_traits : public function_traits<decltype(&Function::operator())> {};


template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
typedef ReturnType (*pointer)(Args...);
typedef const std::function<ReturnType(Args...)> function;
};


template <typename Function>
typename function_traits<Function>::function to_function (Function& lambda) {
return static_cast<typename function_traits<Function>::function>(lambda);
}


template <class L>
struct overload_lambda : L {
overload_lambda(L l) : L(l) {}
template <typename... T>
void operator()(T&& ... values){
// here you can access the target std::function with
to_function(*(L*)this)(std::forward<T>(values)...);
}
};


template <class L>
overload_lambda<L> lambda(L l){
return overload_lambda<L>(l);
}


}

我在我的代码中这样使用它:

Lambda ([ & ](const Vector < int > & val){ 这里有些代码 })(a) ;

PS: 在我的实际情况中,然后将这个 std: : function 对象及其参数保存在一个通用内核对象中,稍后可以通过虚函数根据需要执行该对象。

TL; DR : 使用 CTAD可以完成您要求的任务,这个特性使您能够在调用站点创建预期类型的 std::function,而不需要拼写出模板参数:

foo(std::function([](int arg){ return Bar{}; }));
//  ^^^^^^^^^^^^^ constructor call w/o templates
// std::function<Bar(int)>  will be auto-deduced

演示

如果您对如何模拟这种演绎的机制感兴趣,或者需要使用 pre c + + 17编译器,请查看答案的其余部分。


你可以使用 专题/回顾演出。一旦你有一个像这样的工具

#include <functional>


using namespace std;


template<typename T>
struct memfun_type
{
using type = void;
};


template<typename Ret, typename Class, typename... Args>
struct memfun_type<Ret(Class::*)(Args...) const>
{
using type = std::function<Ret(Args...)>;
};


template<typename F>
typename memfun_type<decltype(&F::operator())>::type
FFL(F const &func)
{ // Function from lambda !
return func;
}

你可以对所有的 lambda 类型说 FFL()把它们转换成正确的 std::function版本

template <typename... Args> void Callback(std::function<void(Args...)> f){
// store f and call later
}


int main()
{
Callback(FFL([](int a, float b){
// do something
}));


return 0;
}

展示

这可能会让你感兴趣: https://gist.github.com/Manu343726/94769034179e2c846acc

这是我一个月前写的一个实验。我们的目标是创建一个类似函数的 C + + 模板,它模拟 Haskell 的部分调用闭包,也就是说,当您使用 n参数调用一个带有 m参数的函数时,自动创建一个 m-n参数闭包。

这是这个实验能够做到的一个例子:

int f( int a, int b, int c, int d)
{
return a+b+c+d;
}


int main()
{
auto foo = haskell::make_function( f );


auto a = foo , 1 , 2 , 3; //a is a closure function object with one parameter


std::cout << a , 4 << std::endl; //Prints 10
}

haskell::make_function使用一些类型特征来处理不同类型的函数实体,包括:

auto f = haskell::make_function( []( int x, int y , int z ){ return x*y*z; } );


auto a = f(1,2); //a is functor with one parameter (Using the alternative C++-like syntax)
auto b = a(3); // b is 6

如您所见,我使用逗号运算符来模拟 Haskell 语法,但是您可以将其更改为调用运算符来实现您的目标语法。

您可以完全自由地对代码进行任何操作(检查许可证)。

在 C + + 17中有构造函数类型演绎。因此,您可以为 std: : function 模板参数保存一些类型。这不是没什么,只是少了一点。

template <typename R, typename...A>
void foo(std::function<R(A...)>) {}
int main() {
foo(std::function([](){}));
}

七年后,也许是当时最简单的解决方案,至今仍然有效。

template< char const * (*name) () >
struct user {
auto id() { return name(); }
} ;

用法

constexpr auto lama () { return "Lama"; }


int main( int , char * [] )
{
auto amuser = user< lama >{} ;
cout << boolalpha << amuser.id() << endl ;
}

Lambda 爱好者也可以享用

 auto cat = [] () constexpr { return "Cat"; } ;
auto sneaky = user< cat >{} ;
cout << boolalpha << sneaky.id() << endl ;

对于 std::result_of,如果只使用函数(不使用 class/struct,’因为声明与 std::function完全不同,而且非常丑陋) ,现在可以将其设置为:


template <typename Func, typename ...Args>
std::result_of_t<Func(Args...)> func(Func function, Args... args) {
/// now do your staff here
}
/// usage:
func([](){printf("lambda function\n"});

使用具有显式模板参数列表(C + + 20特性)的 lambda 表达式,可以更容易地编写函数,如下所示:

template <typename F,typename... T>
auto func(F f, T... values) {
return f(values...);
}
int main() {
auto result = func([]<typename... T>(T...args){
return (...*args);
},1,2,3,6,22);


std::cout<<result<<"\n";
}