2016-04-04 4 views
2

Я пытаюсь порт следующий Scala код C++: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 делает компилятор не может сделать полезный вывод типов, а также позволяет использовать лямбды неудобных (мне нужно явно указать параметр шаблона, как

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, по этому вопросу). Есть ли лучший способ реализовать это?

+1

Что означает этот код Scala? –

+0

@ T.C. Например, если у вас есть список 'nums' (типа' List [Int] '), который содержит' 1, 2, 3' и 'Functor [List]', вы можете сделать «Functor [List] .map (nums) (x => (x + 1) .toString) 'и вернуть новый список, содержащий' "2", "3", "4" '. Это похоже на 'std :: transform'. Синтаксис 'F [_]' в основном означает «F» - это тип, который может «содержать» другой тип. –

+0

Вы связаны с C++ 11 или C++ 14? –

ответ

0

С std::result_of_t и родовыми лямбдах вы будете иметь достаточно хороший вызывающий код:

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 для результирующего типа контейнера, если вы предоставляете специальную специализацию для конкретного контейнера.

+0

Спасибо! Последнее предложение в вашем ответе оказалось чрезвычайно полезным! :) –

0

C++ способ сделать это - определить функцию. Назовите это fmap.

Тогда клиенты, которые хотят его поддерживать, переопределяют fmap как свободную функцию, где аргумент 1 принимает объект-клиент, а аргумент 2 принимает функцию, которая отображает из внутреннего типа в другой тип.

Вы можете написать признак, который выражает «поддерживает fmap» и выполняет «правильное» преобразование типа, но это раздражает и не стоит. «Поддержка fmap» проще и полезнее.

Аналогичным образом для fbind и т. Д.

Смежные вопросы