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

时间:2021-07-13 10:10:46

Basically, what I want to be able to do is take a lambda with any number of any type of parameters and convert it to an std::function. I've tried the following and neither method works.

基本上,我想要做的是取一个带有任意数量的参数的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

The following code does work however, but it is not what I want because it requires explicitly stating the template parameters which does not work for generic code.

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

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

I've been mucking around with functions and templates all evening and I just can't figure this out, so any help would be much appreciated.

整个晚上我都在摆弄函数和模板,我就是搞不清楚,所以我非常感谢大家的帮助。

As mentioned in a comment, the reason I'm trying to do this is because I'm trying to implement currying in C++ using variadic templates. Unfortunately, this fails horribly when using lambdas. For example, I can pass a standard function using a function pointer.

正如在评论中所提到的,我之所以这么做是因为我想用变量模板实现c++中的currying。不幸的是,在使用lambdas时,这一过程非常失败。例如,我可以使用函数指针传递一个标准函数。

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

However, I can't figure out how to pass a lambda to such a variadic function. Why I'm interested in converting a generic lambda into an std::function is because I can do the following, but it ends up requiring that I explicitly state the template parameters to std::function which is what I am trying to avoid.

但是,我不知道如何将lambda传递给这样一个可变的函数。我之所以对将泛型lambda转换为std::函数感兴趣,是因为我可以执行以下操作,但它最终要求我显式地向std::function声明模板参数,这正是我要避免的。

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

6 个解决方案

#1


39  

You can't pass a lambda function object as an argument of type std::function<T> without explicitly specifying the template argument T. Template type deduction tries to match the type of your lambda function to the std::function<T> which it just can't do in this case - these types are not the same. Template type deduction doesn't consider conversions between types.

如果不显式地指定模板参数T. template type演绎尝试将lambda函数的类型与std::function 匹配,在这种情况下它不能这样做——这些类型是不一样的。模板类型演绎不考虑类型之间的转换。

It is possible if you can give it some other way to deduce the type. You can do this by wrapping the function argument in an identity type so that it doesn't fail on trying to match the lambda to std::function (because dependent types are just ignored by type deduction) and giving some other arguments.

如果你能给它一些其他的方法来推断类型是可能的。您可以通过将函数参数包装在标识类型中来实现这一点,这样就不会失败地尝试将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;
}

This is obviously not useful in your situation though because you don't want to pass the values until later.

在这种情况下,这显然是没用的,因为您不希望在以后传递值。

Since you don't want to specify the template parameters, nor do you want to pass other arguments from which the template parameters can be deduced, the compiler won't be able to deduce the type of your std::function argument.

由于您不希望指定模板参数,也不希望传递可以推断模板参数的其他参数,因此编译器无法推断出std:::函数参数的类型。

#2


15  

You can use a dedicated/retrospective cast. Once you have a tool like this

您可以使用专门的/回顾性的cast。一旦你有了这样的工具

#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;
}

you can say FFL() to all lambda types to have them converted to what would be the correct version of std::function

您可以对所有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;
}

Display

显示

#3


12  

As shown at Inferring the call signature of a lambda or arbitrary callable for "make_function", you can infer the calling signature of a lambda (or any other functor with a single calling signature) from its (single) operator():

如在推断lambda的调用签名或“make_function”的任意可调用的调用签名时所示,您可以从lambda(单个)操作符()推断lambda的调用签名(或任何其他具有单个调用签名的函数):

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;

This is a rather inflexible approach, though; as R. Martinho Fernandes says, it won't work for functors with multiple operator()s, nor for functors with templated operator() or for (C++14) polymorphic lambdas. This is why bind defers inference of its result type until the eventual call attempt.

不过,这是一种相当不灵活的做法;正如R. Martinho Fernandes说的那样,它不会对具有多个运算符()s的功能函数,也不会对带有模板操作符()或(c++ 14)多态lambdas的函子工作。这就是为什么bind延迟了其结果类型的推断,直到最终的调用尝试。

#4


7  

It is possible to get the needed std::function type for lambda using derivation, decltype, variadic templates and a few type traits:

可以使用派生、解密、可变类型模板和一些类型特征获得lambda所需的std:::函数类型:

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);
    }

}

I use it in my code like this:

我在我的代码中使用如下:

ambient::lambda([&](const vector<int>& val){ // some code here // })(a);

环境::lambda([&](const vector & val){//这里的一些代码//})(a);

PS: in my real case I then save this std::function object and its arguments inside a generic kernel objects that I can execute later on demand via virtual functions.

在我的真实案例中,我将这个std::函数对象和它的参数保存在一个通用内核对象中,我可以通过虚函数执行稍后的需求。

     

     

#5


3  

Isn't currying already implemented with std::bind?

使用std: bind吗?

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

#6


3  

This could be interesting for you: https://gist.github.com/Manu343726/94769034179e2c846acc

这对您来说可能很有趣:https://gist.github.com/Manu343726/94769034179e2c846acc

That is an experiment I have written a month ago. The goal was to create a functor-like C++ template which emulates Haskell's partial calls closures, i.e. the automatic creation of a closure of m-n argumments when you call with n argumments a function with m parameters.

这是我一个月前写的一个实验。目标是创建一个类似于函数的c++模板,该模板模拟Haskell的部分调用闭包,即在使用n个参数调用具有m参数的函数时自动创建m-n参数闭包。

This is one example of what this experiment is cappable to do:

这是这个实验可以做的一个例子:

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 uses some type traits to take care of the different types of function entities, lambdas included:

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

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

As you can see, I use comma operator to mmimic Haskell syntax, but you could change it to the call operator to achieve your goal syntax.

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

Your are completely free to do anything you want with the code (Check the license).

您完全可以*地使用代码做任何您想做的事情(检查许可证)。

#1


39  

You can't pass a lambda function object as an argument of type std::function<T> without explicitly specifying the template argument T. Template type deduction tries to match the type of your lambda function to the std::function<T> which it just can't do in this case - these types are not the same. Template type deduction doesn't consider conversions between types.

如果不显式地指定模板参数T. template type演绎尝试将lambda函数的类型与std::function 匹配,在这种情况下它不能这样做——这些类型是不一样的。模板类型演绎不考虑类型之间的转换。

It is possible if you can give it some other way to deduce the type. You can do this by wrapping the function argument in an identity type so that it doesn't fail on trying to match the lambda to std::function (because dependent types are just ignored by type deduction) and giving some other arguments.

如果你能给它一些其他的方法来推断类型是可能的。您可以通过将函数参数包装在标识类型中来实现这一点,这样就不会失败地尝试将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;
}

This is obviously not useful in your situation though because you don't want to pass the values until later.

在这种情况下,这显然是没用的,因为您不希望在以后传递值。

Since you don't want to specify the template parameters, nor do you want to pass other arguments from which the template parameters can be deduced, the compiler won't be able to deduce the type of your std::function argument.

由于您不希望指定模板参数,也不希望传递可以推断模板参数的其他参数,因此编译器无法推断出std:::函数参数的类型。

#2


15  

You can use a dedicated/retrospective cast. Once you have a tool like this

您可以使用专门的/回顾性的cast。一旦你有了这样的工具

#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;
}

you can say FFL() to all lambda types to have them converted to what would be the correct version of std::function

您可以对所有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;
}

Display

显示

#3


12  

As shown at Inferring the call signature of a lambda or arbitrary callable for "make_function", you can infer the calling signature of a lambda (or any other functor with a single calling signature) from its (single) operator():

如在推断lambda的调用签名或“make_function”的任意可调用的调用签名时所示,您可以从lambda(单个)操作符()推断lambda的调用签名(或任何其他具有单个调用签名的函数):

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;

This is a rather inflexible approach, though; as R. Martinho Fernandes says, it won't work for functors with multiple operator()s, nor for functors with templated operator() or for (C++14) polymorphic lambdas. This is why bind defers inference of its result type until the eventual call attempt.

不过,这是一种相当不灵活的做法;正如R. Martinho Fernandes说的那样,它不会对具有多个运算符()s的功能函数,也不会对带有模板操作符()或(c++ 14)多态lambdas的函子工作。这就是为什么bind延迟了其结果类型的推断,直到最终的调用尝试。

#4


7  

It is possible to get the needed std::function type for lambda using derivation, decltype, variadic templates and a few type traits:

可以使用派生、解密、可变类型模板和一些类型特征获得lambda所需的std:::函数类型:

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);
    }

}

I use it in my code like this:

我在我的代码中使用如下:

ambient::lambda([&](const vector<int>& val){ // some code here // })(a);

环境::lambda([&](const vector & val){//这里的一些代码//})(a);

PS: in my real case I then save this std::function object and its arguments inside a generic kernel objects that I can execute later on demand via virtual functions.

在我的真实案例中,我将这个std::函数对象和它的参数保存在一个通用内核对象中,我可以通过虚函数执行稍后的需求。

     

     

#5


3  

Isn't currying already implemented with std::bind?

使用std: bind吗?

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

#6


3  

This could be interesting for you: https://gist.github.com/Manu343726/94769034179e2c846acc

这对您来说可能很有趣:https://gist.github.com/Manu343726/94769034179e2c846acc

That is an experiment I have written a month ago. The goal was to create a functor-like C++ template which emulates Haskell's partial calls closures, i.e. the automatic creation of a closure of m-n argumments when you call with n argumments a function with m parameters.

这是我一个月前写的一个实验。目标是创建一个类似于函数的c++模板,该模板模拟Haskell的部分调用闭包,即在使用n个参数调用具有m参数的函数时自动创建m-n参数闭包。

This is one example of what this experiment is cappable to do:

这是这个实验可以做的一个例子:

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 uses some type traits to take care of the different types of function entities, lambdas included:

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

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

As you can see, I use comma operator to mmimic Haskell syntax, but you could change it to the call operator to achieve your goal syntax.

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

Your are completely free to do anything you want with the code (Check the license).

您完全可以*地使用代码做任何您想做的事情(检查许可证)。