C ++ не имеет параметров c полиморфизма, подобного этому - вы не можете делать такие вещи, как «для любого типа», так, как вы хотите, и так, как вы можете в Haskell. Я думаю, что это принципиально невозможно в мире, где существует перегрузка.
У вас есть следующее (я пошел дальше и удалил ошибочное bool
, которое не является частью концепций C ++ 20, и исправил -> Type
, который также был удален):
template<template<typename> class F, typename A, typename B>
concept Functor = requires(std::function<B(A)> f, F<A> fa) {
{ fmap(f, fa) } -> std::same_as<F<B>>;
};
То, что вы хотите сказать, относится к любым типам a
и b
, учитывая a -> b
, вы можете вызвать эту функцию. Мы не можем этого сделать. Но мы можем сами выбирать произвольные типы. Один из способов сделать это - выбрать секретные типы, о которых реализация функтора просто не знает:
namespace secret {
struct A { };
struct B { };
template <typename From, typename To>
struct F {
auto operator()(From) const -> To;
};
}
template <template <typename> class F>
concept Functor = requires(secret::F<secret::A, secret::B> f, F<secret::A> fa) {
{ fmap(f, fa) } -> std::same_as<F<secret::B>>;
};
Возможно, это ваш лучший выбор. Вы можете даже добавить несколько пар a
/ b
, чтобы сделать это с большей вероятностью правильным.
Независимо от этого:
template<Functor F, typename A, typename B>
F<B> replace(B b, F<A> fa);
В любом случае этого не произойдет, поскольку у нас нет такого краткого синтаксиса для ограниченных параметров шаблона шаблона. Вы должны написать это так:
template <template <typename> class F, typename A, typename B>
requires Functor<F>
F<B> replace(B b, F<A> fa);
В качестве примечания, это плохая реализация fmap
для optional
:
template<typename A, typename B>
std::optional<B> fmap(std::function<B(A)> f, std::optional<A> fa);
Взятие std::function<Sig>
означает, что это будет только работать, если вы передадите в конкретно a std::function
. Не для лямбд, указателей функций или других объектов функций (например, secret::F
, который я использовал ранее). И даже если бы это сработало, вы бы не захотели делать это в любом случае, поскольку это ненужные накладные расходы.
Вы хотите:
template <typename F, typename A, typename B = std::invoke_result_t<F&, A const&>>
std::optional<B> fmap(F f, std::optional<A> fa);
У меня есть целый пост об этой конкретной проблеме, Объявления, использующие понятия .