In order to broaden my understanding of C++11, I'm experimenting with writing functional helpers and seeing if I can make calling them less verbose. Consider the following code:
#include <list>
int timesTwo(int x) { return x*2; }
int timesX(int x, int y) { return x*y; }
class Foo {
public:
Foo(int a) : value(a) {};
int fooTimesTwo() const { return value*2; };
int value;
};
template <class T, class U>
std::list<U> myMap(const std::list<T> &list, const std::function<U(const T &)> &func)
{
std::list<U> result;
for(typename std::list<T>::const_iterator it = list.begin(); it != list.end(); ++it) {
result.push_back(func(*it));
}
return result;
}
int main()
{
std::list<int> numbers = {1,2,3,4,5};
std::list<int> numbers2 = myMap<int,int>(numbers, [] (int x) { return x*2; });
std::list<int> numbers3 = myMap<int,int>(numbers, ×Two);
std::list<int> numbers4 = myMap<int,int>(numbers, std::bind(timesX, 2, std::placeholders::_1));
std::list<Foo> fooList = {Foo(1), Foo(2), Foo(3), Foo(4)};
std::list<int> numbers5 = myMap<Foo,int>(fooList, &Foo::fooTimesTwo);
return 0;
}
Is there anyway to rewrite myMap
so that
all four of the calls to it in my code example do not require any template arguments, and...
there's only one general implementation, and I don't have to manually write an overloaded version for each combination of types I want to call it with?
I've tried changing the second argument of myMap
to be a third templated type instead of std::function
, but it fails because a) the second templated type U
can't be inferred, and b) even if it could, the fourth call to myMap
will cause an error on line 20 due to &Foo::fooTimesTwo
not being a function or function pointer.
I'm willing to consider all of the various features of C++11 to do this, and I don't particularly care if it makes the declaration or definition of myMap
obtuse or unreadable. I'm just wondering if it's possible and, if so, what sort of techniques and C++11 features can accomplish it.
You could attempt to implement std::invoke
in C++11, but I think it would be really cumbersome.
It's fairly simple to make a function template for a generalized callable:
template <class T, class F>
auto myMap(const std::list<T> &list, F&& func)
-> std::list<typename std::decay<decltype(func(*list.begin()))>::type>
{
using U = typename std::decay<decltype(func(*list.begin()))>::type;
std::list<U> result;
for(typename std::list<T>::const_iterator it = list.begin(); it != list.end(); ++it) {
result.push_back(func(*it));
}
return result;
}
You get expression sfinae for free. Now there's only member function pointer to be taken care of:
template <class T, class F>
auto myMap(const std::list<T> &list, F&& func)
-> std::list<typename std::decay<decltype(((*list.begin()).*func)())>::type>
{
return myMap(list, [=](T const& t){ return (t.*func)(); });
}
Now you can call your functions as expected. demo
While at it, you could replace this ugly for
loop with a ranged for:
for(auto const& elem : list) {
results.push_back(func(elem));
}
Or use an algorithm:
std::transform(list.begin(), list.end(), std::back_inserter(result), func);