使用模板重载函数类型

Pol*_*mer 6 c++

容器和函数都有一个共同的抽象.我在Haskell中学到了它,我正在尝试用C++实现它.

大多数C++程序员都熟悉std :: transform,粗略地说,从A类到B类的函数,你可以将类型A的容器转换为类型B的容器.

您可以以类似的方式转换函数,给定从A到B的函数foo,您可以将将Z转换为A的函数栏转换为函数foo.将Z带到B.实现很简单,它只是组合.

我想在容器和函数上定义一个函数fmap,以反映泛型编程的这种抽象.

容器很容易(我知道这不完全一般)

template <typename A, typename Func>
auto fmap(Func f, vector<A> in) {
  vector<decltype(f(in[0]))> out_terms{};
  for(auto vec : in)
    out_terms.push_back(f(vec));
  return out_terms;
}
Run Code Online (Sandbox Code Playgroud)

然而,功能的类似功能让我更加紧张.

template <typename FuncT, typename Func>
auto fmap(FuncT f, Func in) {
  return [f, in](auto x){
    return f(in(x));
  };
}
Run Code Online (Sandbox Code Playgroud)

虽然模板不会专门用于除可调用事物之外的任何东西,但我担心这会混淆重载决策.我想在模板参数上引入类型约束,以限制它们对函数类型的解析,以保持名称空间的清洁.我打算问如何做到这一点.

这种抽象非常通用,对于指向值的指针有相应的fmaps,我怀疑它也可能会发生冲突.

所以我认为我的问题是,我可以使用相同的模板级签名进行两种不同的模板实现吗?我几乎可以肯定答案是肯定的,但也许类似的事情可能会被伪造.如果没有,今天可以使用哪些工具来区分过载?特别是对于功能类型.

对我而言,这似乎是概念的教科书案例,尽管我不确定.

编辑:使用Boost是可以接受的,特别是SFINAE.我试图找到一个大多数程序员都熟悉的解决方案,尽可能方便,规范.我可以将fmap重命名为compose,但程序员必须知道将compose传递给接受fmap的模板函数.那将是不幸的,因为fmap在语义上是唯一的.

编辑2:如何使用它的一个简单示例.

template <typename T>
auto double_everything(T in){
  auto doublef = [](auto x){return 2*x;};
  return fmap(doublef, in);
}
Run Code Online (Sandbox Code Playgroud)

它将容器上的映射概括为映射到"容器之类"的东西.因此double_everything(vector<int> {1, 2, 3})返回一个向量,其元素加倍.但double_everything([](int x){ return x + 1; })返回一个函数,其输出是递增函数输出的两倍.这就像加倍一种清单.抽象有一些很好的属性,我不仅仅是弥补它.无论如何,将函数重命名fmap为compose并不能回答这个问题.

编辑3: fmap为模板C从函数需要AB到功能,从C<A>C<B>和满足fmap( compose(f, g) , c ) = fmap( f, fmap( g, c )).这是一个很好的结构保留财产.

对范围执行此操作的函数已经以不同的名称存在.但范围不是类型上的唯一模板.这是fmapstd::optional:

template<typename T, typename Func>
auto fmap(Func f, optional<T> o) -> optional<f(*o)>{
  if(o)
    return f(*o);
  else
    {};
}
Run Code Online (Sandbox Code Playgroud)

此实现根本不涉及任何范围概念,例如fmap前面介绍的for函数.但它满足了语义要求fmap.

我试图定义fmap不同的重载,就像我operator *为自定义矩阵类型定义一个新的一样.所以,我会愉快地定义fmap来讲boost::transform_iterator.然后,这些算法将使用泛型函数fmap.

以下是此类功能的示例:

template < 
  template<typename, typename> class Cont, 
  typename Fmappable, 
  typename Alloc, 
  typename Func>
auto map_one_deep(Func f, Cont<Fmappable, Alloc> c){
  auto g = [f](Fmappable x){ return fmap(f, x); };
  return fmap(g, c);
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我们写

auto lists = vector<vector<int> > { {1, 2, 3}, {4, 5, 6} };
auto lists_squared = map_one_deep( [](int x){return x*x;} , lists);
Run Code Online (Sandbox Code Playgroud)

lists_squared 印刷的

1 4 9
16 25 36
Run Code Online (Sandbox Code Playgroud)

如果我们有一个选项向量,那么只要它们包含元素,选项就会被平方.

我试图理解如何在c ++中使用高阶函数.

Pol*_*mer 0

这是我发现的最简单的妥协

template <typename FuncT, typename O, typename T>
auto fmap(FuncT f, function<O(T)> in){
  return [f, in](T x){
    return f(in(x));
  };
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这需要function<Output(Input)>装饰调用站点,并且它会浪费间接性。我很确定如果需要约束,这是最好的方法。fmap

编辑:你可以做得更好。该链接提供了一种限制可调用对象的方法,该方法也是内联的。

该函数可以这样写:

template <typename FuncT, typename T>
auto fmap(FuncT f, tagged_lambda<T> in){
  return tag_lambda([f, in](T x){
    return f(in(x));
  });
}
Run Code Online (Sandbox Code Playgroud)

您可以通过致电在呼叫站点选择您想要的版本

fmap(g, tag_lambda({}(int x){return x + 1;}) );

或者

fmap(g, function<int(int)>({}(int x){return x + 1;}) );

考虑到模板的工作原理,我非常确定需要标记该函数。

这是一篇博客文章,其中也讨论了这个问题,并讨论了其他选项。 http://yapb-soc.blogspot.com/2012/10/fmap-in-c.html