Reputation: 2532
I'm trying to write a generic fold function using the new anonymous functions available in C++11, here is what I have:
template<typename T>
T foldl(std::function<T(T,T)> f, T initial, std::vector<T> items) {
T accum = initial;
for(typename std::vector<T>::iterator it = items.begin(); it != items.end(); ++it) {
accum = f(accum, (*it));
}
return accum;
}
The following attempt to use it:
std::vector<int> arr;
arr.assign(8, 2);
foldl([] (int x, int y) -> int { return x * y; }, 1, arr);
causes an error:
main.cpp:44:61: error: no matching function for call to 'foldl(main(int, char**)::<lambda(int, int)>, int, std::vector<int>&)'
main.cpp:44:61: note: candidate is:
main.cpp:20:3: note: template<class T> T foldl(std::function<T(T, T)>, T, std::vector<T>)
main.cpp:20:3: note: template argument deduction/substitution failed:
main.cpp:44:61: note: 'main(int, char**)::<lambda(int, int)>' is not derived from 'std::function<T(T, T)>'
It seems to me that using std::function
is not the right way to go about defining the type of f
. How can I correct this?
Upvotes: 10
Views: 8329
Reputation: 122449
If you simply convert it to an std::function
first, and then use that, it will work:
std::vector<int> arr;
arr.assign(8, 2);
std::function<int(int,int)> f = [] (int x, int y) -> int { return x * y; };
foldl(f, 1, arr);
The problem is that lambda is a different type than std::function
. Each lambda is a separate type. Although a lambda (and all other function objects) is convertible to a std::function
with appropriate type parameter, the compiler has no idea what template argument for T
would make it convertible. (We happen to know that T
= int
would work, but there is no general way to figure it out.) So it cannot compile it.
Upvotes: 2
Reputation: 473467
Your code is not very generic. There is no need to require a function
, vector
, or anything of the kind. And generally, in C++, functions would go at the end of the argument list (especially important for lambdas, as they can be big).
So it would be better (ie: more standard) to write this as this:
template<typename Range, typename Accum>
typename Range::value_type foldl(const Range &items, const typename Range::value_type &initial, Accum f)
{
typename Range::value_type accum = initial;
for(const auto &val : items) {
accum = f(accum, val);
}
return accum;
}
Or you could just use std::accumulate
which does the exact same thing.
Upvotes: 15
Reputation: 185681
I'm not certain why this template is failing, but switching over to using a template parameter for the function instead of std::function<>
seems to work wonderfully.
template<typename T, typename F>
T foldl(F f, T initial, std::vector<T> items) {
Upvotes: 4