可调用类型的C ++约束作为模板参数

时间:2016-04-04 23:19:53

标签: c++ scala templates

我正在尝试将以下Scala代码移植到C ++:

trait Functor[F[_]] {
  def map[A, B](fa: F[A])(f: A => B): F[B]
}

我尝试过类似的事情:

template<template <typename> class F>
struct Functor {
    template <typename A, typename B>
    static F<B> map(const F<A> &fa, ??? f);  // What's the type of `f`?
};

但我不知道如何表达f的类型(Scala中的A => BFunction1[A, B])。使用std::function会使编译器无法进行有用的类型推断,也会使lambdas使用笨拙(我需要显式指定模板参数,如

Functor<List>::map<Foo, Bar>(foos, [](const Foo &foo) -> Bar { return foo.toBar(); });

,否则编译器似乎无法推断出B是什么。解决方法似乎是:

template<template <typename> class F>
struct Functor {
    template <typename A, typename A2B>
    static auto map(const F<A> &fa, A2B f) -> F<decltype(f(fa[0]))>;
};

但这看起来很难看,F<A>可能并不总是支持括号运算符(或::value_type,就此而言)。有没有更好的方法来实现这个?

2 个答案:

答案 0 :(得分:0)

使用std::result_of_t和通用lambda,您将拥有足够好的调用代码:

template<template <typename> class F>
struct Functor {    
    template <typename A, typename A2B>
    static auto map(const F<A> &fa, A2B f) -> F<std::result_of_t<A2B(A)>>;
};

auto bars = Functor<std::vector>::map(foos, [](const auto& foo) { return foo.toBar(); });

但请注意,大多数标准C ++容器都有多个模板参数。要使此代码与它们一起使用的最简单方法,您必须将Functor的定义更改为

template<template <typename...> class F>
struct Functor {

您无法为结果容器类型指定其他模板参数,例如Allocator,除非您为特定容器提供专用的专门化。

答案 1 :(得分:0)

C ++的方法是定义一个函数。称之为fmap。

然后,想要支持它的客户端覆盖fmap作为自由函数,其中参数1采用客户端对象,参数2采用从内部类型映射到其他类型的函数。

你可以写一个表达“支持fmap”并进行“正确”类型转换的特性,但它很烦人而且不值得。 “支持fmap”更容易,也很有用。

同样适用于fbind