C++ 如何使用模板将 lambda 转换为 std::function

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13358672/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:14:04  来源:igfitidea点击:

How to convert a lambda to an std::function using templates

c++functiontemplatesc++11lambda

提问by retep998

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::function。我尝试了以下方法,但两种方法都不起作用。

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++ 中实现柯里化。不幸的是,这在使用 lambda 时会失败。例如,我可以使用函数指针传递标准函数。

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::function 感兴趣是因为我可以执行以下操作,但最终要求我将模板参数显式声明为 std::function,这是我试图避免的。

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

采纳答案by Joseph Mansfield

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.

如果std::function<T>不显式指定模板参数,则不能将 lambda 函数对象作为类型参数传递T。模板类型推导尝试将 lambda 函数的类型std::function<T>与其在这种情况下无法执行的类型相匹配- 这些类型不相同。模板类型推导不考虑类型之间的转换。

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 identitytype 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.

如果你能给它一些其他的方式来推断类型,这是可能的。您可以通过将函数参数包装在一个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;
}

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::functionargument.

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

回答by Nikos Athanasiou

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

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

#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

您可以FFL()对所有 lambda 类型说,让它们转换为正确版本的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

展示

回答by ecatmur

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”的任意可调用签名所示,您可以从它的 (single) 推断 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;

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 templatedoperator()or for (C++14) polymorphic lambdas. This is why binddefers inference of its result type until the eventual call attempt.

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

回答by Alex Kosenkov

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

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

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

ambient::lambda([&](const vector<int>& val){ // some code here // })(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.

PS:在我的真实情况下,我将这个 std::function 对象及其参数保存在一个通用内核对象中,我可以稍后通过虚拟函数按需执行。

     

     

回答by xtofl

Isn't currying alreadyimplemented 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 );

回答by Manu343726

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-nargumments when you call with nargumments a function with mparameters.

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

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_functionuses 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 语法,但您可以将其更改为调用运算符以实现您的目标语法。

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

你可以完全自由地用代码做任何你想做的事情(检查许可证)。

回答by user2281723

In C++17 there is the constructor type deduction. So you can save some typing for the std::function template arguments. This is not quite nothing, but a bit less.

在 C++17 中有构造函数类型推导。因此,您可以为 std::function 模板参数节省一些输入。这不是完全没有,而是少了一点。

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

回答by Chef Gladiator

Seven years later and probably the simplest solution then, still works today.

七年后,可能是当时最简单的解决方案,今天仍然有效。

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

Usage

用法

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

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

Lambda afficionados are served too

也为 Lambda 爱好者提供服务

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