如何使函数<>参数的C++ 11函数自动接受lambdas

tin*_*lyx 7 c++ lambda c++11

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

 #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;
}
Run Code Online (Sandbox Code Playgroud)

,直接使用lambda作为main()的第2行将无法正常工作.g++ -std=c++11 testfunc.cpp返回`... testfunc.cpp:14:37:注意:'main():: __ lambda0'不是从'std :: function'派生的.

C++ 11类型的推理也失败了,你可以看到,如果将lambda存储到auto变量然后使用它,类型信息仍然会丢失,可能是由于类型擦除和性能损失小的原因(因为我是告诉:为什么在c ++ 11中lambda函数没有函数<>类型?).

什么工作是将lambda存储在std:function <>类型变量中并使用该变量.这是相当不方便的,并且在C++ 11中失败了在函数式编程中使用lambda的目的.例如,人们无法使用bind或flip之类的东西来操纵lambda,而是必须先将lambda存储到变量中.

我的问题是,是否有可能(以及如何)克服这个问题并使main()的第2行合法,例如通过覆盖一些类型转换运算符?(当然,这意味着我不关心使用/不使用类型擦除所涉及的小的性能损失.)

提前致谢.

---编辑---

为了澄清,我使用std::function而不是函数参数的泛型类型参数的原因是std::function具有确切的类型信息,而泛型类型参数template <typename F> map(F f, ...)不包含类型信息.而且,正如我最终想到的那样,每个lambda都是它自己的类型.因此类型擦除甚至不是lambda与其匹配std::function对象之间不兼容的问题.

---更新---

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

- 更新2 ---

在回应注释时,这里是一个实例的例子,其中std :: function <> COULD中的类型信息很有用.假设我们想fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b在OCaml中实现C++等价物(http://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html).

使用std :: function <>,可以做到

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

从上面可以清楚地看出它是什么f,它能够或不能采取什么.也许,人们也可以使用

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

但是,当你试图弄清楚要放入什么时,这会变得有点难看decltype.此外,究竟f需要做什么,以及使用的正确方法是f什么?从可读性的角度来看,我想代码的读者只能通过解释函数体中的实现来找出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);
Run Code Online (Sandbox Code Playgroud)

抛开效率问题,上面的代码很不方便BECAUSE std :: function不能接受lambda的.所以,

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

目前在C++ 11中不起作用.我的问题是关于是否有可能使fold_right上面定义的函数直接接受lambda.也许这太希望了.我希望这澄清了这个问题.

Die*_*ühl 7

为什么要首先创建动态间接通道std::function<...>?只是对函数对象进行模板化,然后对其进行排序:

template <typename A, typename F> 
auto map(F f, std::vector<A> arr) -> std::vector<decltype(f(arr[0]))> {
    std::vector<decltype(f(arr[0]))> res;
    for (int i=0; i<arr.size(); ++i)
        res.push_back(f(arr[i]));
    return res;
}
Run Code Online (Sandbox Code Playgroud)

实际上,也没有任何需要固定容器类型的东西,你可能也希望通过[ const]引用传递它:

template <typename C, typename F> 
auto map(F f, C const& c) -> std::vector<decltype(f(*c.begin()))> {
    std::vector<decltype(f(*c.begin()))> res;
    for (auto const& value: c)
        res.push_back(f(value));
    return res;
}
Run Code Online (Sandbox Code Playgroud)

最后,请注意标准C++库已经作为"map"函数.它碰巧是拼写的,std::transform()并且有一个更适合C++通用方法的接口:

std::vector<int> result;
std::transform(a.begin(), a.end(), std::back_inserter(result),
               [](int x){ return x;});
Run Code Online (Sandbox Code Playgroud)


Pup*_*ppy 5

您的地图功能已损坏.std::function除非您不能使用模板,否则请勿使用; 在这种情况下,你肯定可以.您不需要B作为模板参数,因为decltype可以将它提供给您,并且您根本不需要参数类型std::function.

template <typename A, typename F> auto map(F f, vector<A> arr) -> std::vector<decltype(f(arr.front())> {
    std::vector<decltype(f(arr.front())> res;
    for (int i=0;i<arr.size();i++) res.push_back(f(arr[i]));
    return res;
}
Run Code Online (Sandbox Code Playgroud)

为了记录,这忽略了地图功能的其他一切错误.


tin*_*lyx 5

最后找出了一个通用的包装函数make_function(在当前的c ++ 11中),用于将任何lambda转换为std::function带有类型推导的相应对象.现在而不是使用ctor:

map(function<int (int)>( [](int x) -> int { return x;} ), {1,2,3});
Run Code Online (Sandbox Code Playgroud)

这需要两次提供相同类型的信息,以下简洁形式有效

map(make_function([](int x) -> int { return x;}),a); //now OK
Run Code Online (Sandbox Code Playgroud)

代码如下:

 #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;
}
Run Code Online (Sandbox Code Playgroud)

- 原始答案 -

在经过几周的搜索(并且因使用std :: function <>作为参数而受到惩罚)之后回答我自己的问题,可能是我能找到函数<>的最佳方法 - 类型参数接受lambda(在c ++中) 11)只是通过显式演员:

map((function<int (int)>) ([](int x) -> int { return x;} ), {1,2,3});
Run Code Online (Sandbox Code Playgroud)

或使用ctor:

map(function<int (int)>( [](int x) -> int { return x;} ), {1,2,3});
Run Code Online (Sandbox Code Playgroud)

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

希望,当lambdas可以正确输入或更好地进行类型推导时,c ++ 14/1y中的事情会有所改善.