Just use boost :: adapters :: tranformed :
#include <boost/range/adaptor/transformed.hpp> template <typename Collection, typename function> auto map(function f, Collection c) { return c | boost::adaptors::transformed(f); }
In this range, you can create any container you want.
char first_letter(string s) { return s[0]; } vector<string> words; words.push_back("hello"); words.push_back("world"); auto transformed_range = map(first_letter, words); vector<char> first_letters(begin(transformed_range ), end(transformed_range ));
If you insist that the map function return a container, not a range, add another parameter to this function template:
#include <boost/range/adaptor/transformed.hpp> template <typename Result, typename Collection, typename function> auto map(function f, Collection c) { auto transformed_range = c | boost::adaptors::transformed(f); return Result(begin(transformed_range), end(transformed_range)); } char first_letter(string s) { return s[0]; } vector<string> words; words.push_back("hello"); words.push_back("world"); vector<char> first_letters = map<vector<char>>(first_letter, words);
But if you really insist on having the exact behavior as you want, you must have some traits in knowing how to convert a collection type to another collection type with a converted value.
First up is the way to have new_value_type:
template <typename Function, typename OldValueType> struct MapToTransformedValue { using type = decltype(std::declval<Function>()(std::declval<OldValueType>())); };
Common feature:
template <typename Function, typename Container> struct MapToTransformedContainer;
The simplest case is for std::array :
// for std::array template <typename Function, typename OldValueType, std::size_t N> struct MapToTransformedContainer<Function, std::array<OldValueType, N>> { using value_type = typename MapToTransformedValue<Function, OldValueType>::type; using type = std::array<value_type, N>; };
For std::vector - a little more complicated - you need to provide a new dispenser, for std allocators - you can use its reprocessing template:
// for std::vector template <typename Function, typename OldValueType, typename OldAllocator> struct MapToTransformedContainer<Function, std::vector<OldValueType, OldAllocator>> { using value_type = typename MapToTransformedValue<Function, OldValueType>::type; using allocator = typename OldAllocator::template rebind<value_type>::other; using type = std::vector<value_type, allocator>; };
So your function will look like this:
template <typename Collection, typename function> auto map(function f, Collection c) { using NewCollectionType = typename MapToTransformedContainer<function, Collection>::type; auto transformed_range = c | boost::adaptors::transformed(f); return NewCollectionType (begin(transformed_range), end(transformed_range)); }
Now - your main() optionally:
char first_letter(std::string const& s) { return s[0]; } int main() { std::vector<std::string> words; words.push_back("hello"); words.push_back("world"); auto first_letters = map(first_letter, words); std::cout << first_letters[0] << std::endl; }
Beware that for other containers where value_type consists of Key,Value , a pair like std::map , std::set (and their unordered _... siblings), you must define a different specialization MapToTransformedContainer ...