我正在尝试将以下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 => B
或Function1[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
,就此而言)。有没有更好的方法来实现这个?
答案 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
等