如何使C ++ 11函数具有函数<>参数自动接受lambdas [英] How to make C++11 functions taking function<> parameters accept lambdas automatically

查看:177
本文介绍了如何使C ++ 11函数具有函数<>参数自动接受lambdas的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C ++ 11同时具有lambda和std :: function<>,但不幸的是,它们有不同的类型。
一个结果是,不能在更高阶函数中直接使用lambda,例如lisp中的map。例如,在以下代码中

  #include< vector> 
#include< functional>
using namespace std;

template< typename A,typename B>
矢量< B> map(std :: function< B(A)> f,vector< A> arr){
vector& res;
for(int i = 0; i return res;
}

int main(){
vector< int> a = {1,2,3};
map([](int x) - > int {return x;},a); // not OK

auto id_l = [](int x) - > int {return x;};
map(id_l,a); //不好;

function< int(int)> id_f = id_l;
map(id_f,a); // OK
return 0;
}

,直接使用main() 。 g ++ -std = c ++ 11 testfunc.cpp 返回`... testfunc.cpp:14:37:note:'main():: __ lambda0'从'std :: function'。



C ++ 11类型的引用也失败了,你可以看到如果一个存储lambda到一个自动变量,然后使用它,类型信息仍然丢失,可能是由于类型擦除和小的性能损失的原因(正如我被告知:为什么C ++ 11中的lambda函数不具有函数<> types?)。



什么工作是将lambda存储在std:function<> typed变量中,并使用该变量。这是相当不方便的,在C ++ 11中使用lambda在函数式编程中的目的。例如,不能使用绑定或翻转的东西来操作lambda,而是必须首先将lambda存储到变量。



我的问题是,它是可能的(和如何)克服这个问题,并使main()的第2行合法,例如通过覆盖一些类型转换运算符? (当然,这意味着我不在乎使用/不使用类型擦除的小性能损失。)



提前感谢。



--- EDIT ---



为了澄清,我使用 std :: function 而不是函数参数的泛型类型参数是 std :: function 有确切的类型信息,而泛型类型参数如 template< typename F> map(F f,...)不包含类型信息。此外,因为我终于弄清楚了,每个lambda是它自己的类型。所以类型擦除甚至在lambda和其匹配的 std :: function 对象之间不兼容的问题。



---更新---



已经有两个答案关于如何使地图功能上面工作或如何改善它们。只是为了澄清。我的问题不是如何使地图工作。有很多其他的使用案例涉及使用std :: function<>类型的参数,我认为至少可以使代码更可读,使类型的引用容易。到目前为止的答案是关于如何不使用std :: function<>作为参数。我的问题是如何使这样的函数(使用std :: function<>类型参数)自动接受lambda。



- 更新2 ---



响应注释,这里是一个实例,其中std :: function<> COULD中的类型信息是有用的。假设我们要实现 fold_right:('a - >'b - >'b) - > 'a list - > 'b - > 'b http:/ /caml.inria.fr/pub/docs/manual-ocaml/libref/List.html )。



使用std :: function<>,一个可以做

  //方法#1 
template< typename A,typename B&
B fold_right(std :: function< B(A,B)> f,vector< A> arr,B b){
...
}

从上面可以看出 f 是什么,不能采取。也许还可以使用

  //方法#2 
模板< typename A,typename B,typename F& ;
auto fold_right2(F f,vector< A> arr,B b) - > decltype(f(???)){
...
}

但是,当你试图找出在 decltype 中放置什么时,这变得很丑陋。另外, f 究竟采取什么,以及使用 f 的正确方法是什么?从可读性的角度来看,我猜测代码的读者只能通过在函数体中执行INTERPRETING来确定什么是f(函数或标量)和签名f。



这是我不喜欢的,这就是我的问题所在。如何使方法#1工作方便。例如,如果 f 表示添加两个数字,如果您先创建一个函数对象,则方法#1将起作用:

  std :: function< int(int,int)> add = [](int x,int y) - > int {return x + y; } 
fold_right(add,{1,2,3},0);效率问题,上面的代码不方便BECAUSE std :: function不能接受lambda的。所以,

  fold_right([](int x,int y) - > int {return x + y;}, 1,2,3},0); 

目前无法在C ++ 11中使用。我的问题具体是如果可以使像 fold_right 定义的函数直接接受lambda。也许太多了,希望。

解决方案

最后找出了一个通用的封装函数 make_function (在当前c ++ 11中)用于将任何lambda转换为其类型扣除的对应的 std :: function 对象。现在不使用ctor:

  map(function< int(int)>([ int {return x;}),{1,2,3}); 

这需要两次提供相同的类型信息,下面的简洁表单工作

  map(make_function([](int x) - > int {return x;}),a); // now OK 

代码如下:

  #include< vector> 
#include< functional>
using namespace std;

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

模板< typename ClassType,typename ReturnType,typename ... Args>
struct function_traits< ReturnType(ClassType :: *)(Args ...)const> {
typedef function< ReturnType(Args ...)> f_type;
};

template< typename L>
typename function_traits< L> :: f_type make_function(L l){
return(typename function_traits L :: fl_type)(l);
}

template< typename A,typename B>
矢量< B> map(std :: function< B(A)> f,vector< A> arr){
vector& res;
for(int i = 0; i return res;
}

int main(){
vector< int> a = {1,2,3};
map(make_function([](int x) - > int {return x;}),a); // now OK
return 0;
}

- 原始答案 -



为了回答我自己的问题后几个星期的搜索(并得到chastised使用std :: function<>作为参数),可能是最好的方式我可以找到有函数<> - 类型参数接受lambda (在c ++ 11中)简单地通过显式转换:

  map((function< int(int)>) [](int x) - > int {return x;}),{1,2,3}); 

或使用ctor:

  map(function< int(int)>([](int x) - > int {return x;}),{1,2,3}); 

为了比较,如果你有一个函数带std :: string(例如 void ff(string s){...} ),它可以自动使用 const char * 。 ( ff(Hi)将工作)。从lambda到 std :: function<> 的自动转换在c ++ 11(这是不幸的,IMO)中不会类似地工作。



有希望的是,当lambdas可以正确输入或更好的类型推导时,在c ++ 14 / 1y中会有所改进。


C++11 has both lambda's and std::function<>, but unfortunately, they have different types. One consequence is that one cannot directly use lambda's in higher order functions such as map in lisp. For example, in the following code

 #include <vector>
 #include <functional>
 using namespace std;

 template <typename A,typename B> 
 vector<B> map(std::function<B (A)> f, vector<A> arr) {
       vector<B> res;
       for (int i=0;i<arr.size();i++) res.push_back(f(arr[i]));
       return res;
}

int main () {
    vector<int> a = {1,2,3};
    map([](int x) -> int { return x;},a); //not OK

    auto id_l = [](int x) -> int { return x;};
    map(id_l,a); //not OK;

    function<int (int)> id_f = id_l;
    map(id_f,a); //OK
return 0;
}

, directly using lambda as in line 2 of main() won't work. g++ -std=c++11 testfunc.cpp returns `... testfunc.cpp:14:37: note: 'main()::__lambda0' is not derived from 'std::function'.

C++11 type inferencing fails as well, as you can see if one stores the lambda to an auto variable and then use it, the type information is still lost, probably due to type erasure and reasons of small performance penalty (as I was told: why do lambda functions in C++11 not have function<> types?).

What does work is to store the lambda in a std:function<> typed variable and use that variable. This is rather inconvenient and kind of defeats the purpose of using lambda's in functional programming in C++11. For example, one cannot manipulate lambda's in place with stuff like bind or flip, and instead has to store the lambda to a variable first.

My question is, is it possible (and how) to overcome this issue and make line#2 of main() legal, e.g. by overwriting some typecast operators? (Of course, this means I don't care about the small performance penalty involved with using/not using type erasure.)

thanks in advance.

--- EDIT ---

To clarify, the reason I use std::function rather than a generic type parameter for the functional parameter is that std::function has exact type information, while a generic type parameter as in template <typename F> map(F f, ...) contains no type information. Also, as I finally figured out, each lambda is its own type. So type erasure wasn't even a issue in the incompatibility between a lambda and its matching std::function object.

---Update---

There are already two answers about how to make the map function above work or how to improve them. Just to clarify. My question isn't about how to make map work. There are plenty of other use cases involving using the std::function<> typed parameters, which I think can at least make the code more readable and make type inferencing easy. The answers so far are about how not to use std::function<> as parameters. My question was about how to make such a function (with std::function<> typed parameters) accept lambda's automatically.

-- Update 2 ---

In response to comments, here is a example of practical case where the type information in std::function<> COULD be useful. Suppose we want to implement a C++ equivalent of fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b in OCaml (http://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html).

With std::function<>, one can do

 //approach#1
 template <typename A,typename B> 
 B fold_right(std::function<B (A, B)> f, vector<A> arr, B b) {
     ...
 }

It is clear from above what f is, and what it can or cannot take. Maybe, one can also use

 //approach#2
 template <typename A,typename B, typename F> 
 auto fold_right2(F f, vector<A> arr, B b) -> decltype(f(???)) {
      ...
 }

But, this is becoming kind of ugly as you try to figure out what to put in the decltype. Also, what exactly does f take, and what's the correct way to use f? From the point view of readability, I guess the reader of the code can only figure out what is f (a function or scalar) and the signature of f by INTERPRETING the implementation in the function body.

That is what I don't like and that's where my question comes from. How to make approach#1 work conveniently. For example, if f represents addition of two numbers, approach#1 works if you create a function object first:

std::function<int (int, int)> add = [](int x, int y) -> int { return x + y; }
fold_right(add,{1,2,3},0);

Efficiency issues aside, the above code is inconvenient BECAUSE std::function cannot accept lambda's. So,

fold_right([](int x, int y) -> int { return x + y; },{1,2,3},0);

will not work currently in C++11. My question is specifically about if it is possible to make functions like fold_right defined above accept lambda's directly. Maybe it's too much to hope for. I hope this clarifies the question.

解决方案

Finally figured out a generic wrapper function make_function (in current c++11) for converting any lambda to its corresponding std::function object with type deduction. Now instead of using ctor:

map(function<int (int)>( [](int x) -> int { return x;} ), {1,2,3});

which requires giving the same type information twice, the following succinct form works

map(make_function([](int x) -> int { return x;}),a); //now OK

Code is below:

 #include <vector>
 #include <functional>
 using namespace std;

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

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

 template <typename L> 
 typename function_traits<L>::f_type make_function(L l){
   return (typename function_traits<L>::f_type)(l);
 }

 template <typename A,typename B> 
 vector<B> map(std::function<B (A)> f, vector<A> arr) {
       vector<B> res;
       for (int i=0;i<arr.size();i++) res.push_back(f(arr[i]));
       return res;
}

int main () {
    vector<int> a = {1,2,3};
    map(make_function([](int x) -> int { return x;}),a); //now OK
    return 0;
}

--original answer--

To answer my own question after a couple of weeks' search (and getting chastised for using std::function<> as parameters), probably the best way I can find to have function<>-typed parameters accept lambda's (in c++11) is simply via explicit cast:

map((function<int (int)>) ([](int x) -> int { return x;} ), {1,2,3});

Or using ctor:

map(function<int (int)>( [](int x) -> int { return x;} ), {1,2,3});

For comparison, if you have a function taking std::string (e.g. void ff(string s) {...}), it can take const char* automatically. (ff("Hi") would work). The automatic conversion from lambda to std::function<> does not similarly work in c++11 (, which is unfortunate, IMO).

Hopefully, things will improve in c++14/1y when lambdas can be properly typed or better type-deduced.

这篇关于如何使C ++ 11函数具有函数&lt;&gt;参数自动接受lambdas的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆