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.也许这太希望了.我希望这澄清了这个问题.
为什么要首先创建动态间接通道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)
您的地图功能已损坏.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)
为了记录,这忽略了地图功能的其他一切错误.
最后找出了一个通用的包装函数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中的事情会有所改善.
| 归档时间: |
|
| 查看次数: |
1525 次 |
| 最近记录: |