Both to teach myself about implementing more advanced template constructions than simply basic ones, and becouse they are useful in many circumstances, I'm trying to implement map, filter and similar functions common in functional programming using c++11 constructs like decltype.
I'm having trouble creating a function prototype that the compiler I use can handle, so I have to ask you how you would create something like this:
//
// Takes an iterable, applies a function to every element, and returns a vector of the results
//
template <typename T, typename Func>
auto map(const T& iterable, Func func) -> std::vector< decltype( func( *iterable.cbegin() ) ) >
{
// body snipped
}
That is, this function should take any iterable and a function that takes the iterables value type as an argument and returns some sort of value. The result of the function call will be a vector, regardless of the type of iterable passed in, of the type that the passed function returns.
The map function should accept any function with a valid prototype as argument, whether it is a functionpointer, a functor or lambda expression.
Using the function above with this test code:
std::vector<int> intVector;
intVector.push_back(1);
intVector.push_back(2);
map(intVector, [](int& value) { return value + 1; });
makes visual studio spit out a C2893 ("Failed to specialize function template") error and I'm not sure what's wrong.
Update: Applied changes suggested in comments and answers so far to question, new prototype tested but the same error remains.
This might do what you want. It uses std::transform
internally, which basically does the whole work. The function I wrote is nothing more than a simple wrapper for containers (not working with C-style arrays, that would require some additional type traits):
#include <vector>
#include <algorithm>
#include <type_traits>
//
// Takes an iterable, applies a function to every element,
// and returns a vector of the results
//
template <typename T, typename Func>
auto map_container(const T& iterable, Func&& func) ->
std::vector<decltype(func(std::declval<typename T::value_type>()))>
{
// Some convenience type definitions
typedef decltype(func(std::declval<typename T::value_type>())) value_type;
typedef std::vector<value_type> result_type;
// Prepares an output vector of the appropriate size
result_type res(iterable.size());
// Let std::transform apply `func` to all elements
// (use perfect forwarding for the function object)
std::transform(
begin(iterable), end(iterable), res.begin(),
std::forward<Func>(func)
);
return res;
}
However, notice that your lambda should take a reference to const
, or better should take its argument by value in the case of int
.
Also, I renamed the function from map
into map_container
: it is a bad programming practice to reuse names of standard containers of the C++ Standard Library for functions, variables, or anything else in your program.
For me, this gives the desired output:
#include <iostream>
int main()
{
std::vector<int> intVector;
intVector.push_back(1);
intVector.push_back(2);
auto v = map_container(intVector, [] (int value) { return value + 1; });
for (int i : v) { std::cout << i << " "; }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With