在C++中实现Haskell风格Functor Concept的问题与改进咨询
基于Haskell Functor的C++ Concept实现问题
初始尝试与问题
近期我尝试基于Haskell的Functor在C++中创建对应的Concept,初始代码如下:
template <template<class>class F> concept Functor = requires (F<A> f) { { fmap(function, f) } -> std::same_as<F<decltype(function(std::declval<A>))>>; };
该代码存在function与A类型的依赖问题:若将它们加入模板声明,Functor Concept将不仅依赖实际的函子F,还会依赖这两个类型。我想知道这类需求在C++中是否可行,同时希望让已定义fmap的static_assert(Functor<std::optional<int>>)能够编译通过。
补充说明
因评论区存在误解,特此澄清:本文所指Functor为Haskell中的函子,即实现如下函数的类型:
fmap :: Functor f => (a -> b) -> f a -> f b
而非C++中重载operator()的类型。可将其理解为无需手动解包即可将std::optional<int>转换为std::optional<std::string>的方法。
可行方案
目前我已取得一定进展,写出了如下代码:
template <template<class, class...> class F, class T> struct Functor { template <class Callable> [[nodiscard]] static constexpr auto fmap(Callable callable, F<T>&& f) -> F<std::decay_t<decltype(callable(std::declval<T>))>>; }; template <template<class, class...> typename F, class T> concept FunctorConcept = requires (F<T>&& f) { { Functor<F, T>::fmap(std::identity{}, std::forward<F<T>>(f)) } -> std::same_as<F<std::decay_t<decltype(std::identity{}(std::declval<T>))>>>; }; template <class T> struct Functor<std::optional, T> { template <class Callable> [[nodiscard]] constexpr static auto fmap(Callable callable, std::optional<T>&& f) -> std::optional<std::decay_t<decltype(callable(std::declval<T>))>> { if (f) return callable(*std::forward<std::optional<T>>(f)); return std::nullopt; } }; template <class T> struct Functor<std::vector, T> { template <class Callable> [[nodiscard]] constexpr static auto fmap(Callable callable, std::vector<T>&& f) -> std::vector<std::decay_t<decltype(callable(std::declval<T>))>> { std::vector<std::decay_t<decltype(callable(std::declval<T>))>> result; std::transform(begin(std::forward<std::vector<T>>(f)), end(std::forward<std::vector<T>>(f)), std::back_inserter(result), callable); return result; } }; int main() { static_assert(FunctorConcept<std::optional, int>); static_assert(FunctorConcept<std::optional, float>); static_assert(FunctorConcept<std::vector, float>); return 0; }
我通过独立结构体处理fmap重载,仅用std::identity验证Concept,但仍对std::identity的使用方式不满意,同时担忧fmap定义的返回类型与Concept不一致易导致代码出错,特此咨询这两点的改进方案。
内容的提问来源于stack exchange,提问作者Dragomir
相关产品推荐
相关产品推荐

