2013-10-24 4 views
4

Предположим, я хочу реализовать общую функцию Map более высокого порядка в C++. Map должен взять контейнер и функцию преобразования и вернуть контейнер того же типа, но, возможно, с разными типами элементов.Ограничения на типы шаблонов

Давайте vector, например:

template <typename InT, typename OutT, typename Tr> 
vector<OutT> Map(vector<InT> cont, Tr tr) 
{ 
    OutCont out(cont.size()); 
    auto oit = out.begin(); 
    for (auto it = cont.cbegin(); it != cont.cend(); ++it, ++ot) 
    { 
     *oit = tr(*it); 
    } 
} 

, которые я хочу использовать так:

vector<int> v(10); 
std::iota(v.begin(), v.end(), 0); 
auto x = Map(v, [](int x) -> int {return x * 2;}); 

Это терпит неудачу в VC++ 2012, что дает мне следующую ошибку:

error C2783: 'std::vector<OutT> Map(std::vector<_Ty>,Tr)' : could not deduce template argument for 'OutT' 

Мне кажется, что компилятор имеет всю необходимую информацию, потому что я явно определил тип возврата в лямбда. Есть ли способ обойти это?

В приведенном выше примере используется vector. Есть ли способ использовать общий тип, чтобы типы ввода и вывода были одинаковыми? Например, если у меня есть контейнер ввода, определенный как vector<string> и функция преобразования tr(string a) -> int, тогда моя цель - заставить компилятор определить тип вывода vector<int>. Вот псевдо-код для того, что я хочу добиться:

template <typename Cont<InT>, typename Cont<OutT>, typename Tr<InT, OutT>> 
Cont<OutT> Map(Cont<InT> cont, Tr<InT, OutT> tr) 
{ 
    // Implementation 
} 
+2

неродственных, но именовании функции 'map' немного сбивает с толку , заданный 'std :: map'. – juanchopanza

+2

Функциональное программирование 'map' ==' std :: transform'. Итераторы действительно позволяют вам выполнять общее программирование над контейнерами в C++: используйте их. – Yuushi

+0

Спасибо. Я изменил его на «Карта». Следует устранить путаницу. – Max

ответ

4

Вы можете написать что-то вроде:

template <typename InT, typename Tr> 
auto Map(std::vector<InT> cont, Tr tr) -> std::vector<decltype(tr(cont[0]))> 
{ 
    std::vector<decltype(tr(cont[0]))> out(cont.size()); 
    auto oit = out.begin(); 
    for (auto it = cont.cbegin(); it != cont.cend(); ++it, ++oit) 
    { 
     *oit = tr(*it); 
    } 
    return out; 
} 

Out тип выводится.

[Редактировать] Для более обобщенной функции с большим контейнером:

template <template<typename, typename...> class Container, typename InT, typename Tr, typename... Args> 
auto Map(const Container<InT, Args...>& cont, Tr tr) -> Container<decltype(tr(cont[0])), Args...> 
{ 
    Container<decltype(tr(cont[0])), Args...> out(cont.size()); 
    auto oit = out.begin(); 
    for (auto it = cont.cbegin(); it != cont.cend(); ++it, ++oit) 
    { 
     *oit = tr(*it); 
    } 
    return out; 
} 

Обратите внимание на typename... необходимо потому, что std::vector может также принимать Allocator

+0

Спасибо. Это было то, что мне нужно. Тем не менее, возникает вопрос об использовании этого для родового типа, а не только для вектора. – Max

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