I'm learning C++, and I'm trying to implement a binary search function that finds the first element for which a predicate holds. The function's first argument is a vector and the second argument is a function that evaluates the predicate for a given element. The binary search function looks like this:
template <typename T> int binsearch(const std::vector<T> &ts, bool (*predicate)(T)) {
...
}
This works as expected if used like this:
bool gte(int x) {
return x >= 5;
}
int main(int argc, char** argv) {
std::vector<int> a = {1, 2, 3};
binsearch(a, gte);
return 0;
}
But if I use a lambda function as a predicate, I get a compiler error:
search-for-a-range.cpp:20:5: error: no matching function for call to 'binsearch'
binsearch(a, [](int e) -> bool { return e >= 5; });
^~~~~~~~~
search-for-a-range.cpp:6:27: note: candidate template ignored: could not match 'bool (*)(T)' against '(lambda at
search-for-a-range.cpp:20:18)'
template <typename T> int binsearch(const std::vector<T> &ts,
^
1 error generated.
The above error is generated by
binsearch(a, [](int e) -> bool { return e >= 5; });
What's wrong? Why is the compiler not convinced that my lambda has the right type?
Your function binsearch takes a function pointer as argument. A lambda and a function pointer are different types: a lambda may be considered as an instance of a struct implementing operator().
Note that stateless lambdas (lambdas that don't capture any variable) are implicitly convertible to function pointer. Here the implicit conversion doesn't work because of template substitution:
#include <iostream>
template <typename T>
void call_predicate(const T& v, void (*predicate)(T)) {
std::cout << "template" << std::endl;
predicate(v);
}
void call_predicate(const int& v, void (*predicate)(int)) {
std::cout << "overload" << std::endl;
predicate(v);
}
void foo(double v) {
std::cout << v << std::endl;
}
int main() {
// compiles and calls template function
call_predicate(42.0, foo);
// compiles and calls overload with implicit conversion
call_predicate(42, [](int v){std::cout << v << std::endl;});
// doesn't compile because template substitution fails
//call_predicate(42.0, [](double v){std::cout << v << std::endl;});
// compiles and calls template function through explicit instantiation
call_predicate<double>(42.0, [](double v){std::cout << v << std::endl;});
}
You should make your function binsearch more generic, something like:
template <typename T, typename Predicate>
T binsearch(const std::vector<T> &ts, Predicate p) {
// usage
for(auto& t : ts)
{
if(p(t)) return t;
}
// default value if p always returned false
return T{};
}
Take inspiration from standard algorithms library.
The lambda expression with empty capture list could be implicitly converted to a function pointer. But the function pointer predicate is taking T as its parameter, that need to be deduced. Type conversion won't be considered in template type deduction, T can't be deduced; just as the error message said, candidate template (i.e. binsearch) is ignored.
You can use operator+ to achieve this, it'll convert lambda to function pointer, which will be passed to binsearch later and then T will be successfully deduced[1].
binsearch(a, +[](int e) -> bool { return e >= 5; });
// ~
Of course you could use static_cast explicitly:
binsearch(a, static_cast<bool(*)(int)>([](int e) -> bool { return e >= 5; }));
Note if you change predicate's type to be independent of T, i.e. bool (*predicate)(int), passing lambda with empty capture list would work too; the lambda expression will be converted to function pointer implicitly.
Another solution is changing the parameter type from function pointer to std::function, which is more general for functors:
template <typename T> int binsearch(const std::vector<T> &ts, std::function<bool (typename std::vector<T>::value_type)> predicate) {
...
}
then
binsearch(a, [](int e) -> bool { return e >= 5; });
[1] A positive lambda: '+[]{}' - What sorcery is this?
Why is the compiler not convinced that my lambda has the right type?
Template functions being told to deduce their template parameters do no conversion. A lambda is not a function pointer, so there is no way to deduce the T in that argument. As all function arguments independently deduce their template parameters (unless deduction is blocked), this results in an error.
There are a number of fixes you can do.
You can fix the template function.
template <class T>
int binsearch(const std::vector<T> &ts, bool (*predicate)(T))
Replace the function pointer with a Predicate predicate or Predicate&& predicate and leave the body unchanged.
template <class T, class Predicate>
int binsearch(const std::vector<T> &ts, Predicate&& predicate)
Use deduction blocking:
template<class T>struct tag_t{using type=T;};
template<class T>using block_deduction=typename tag_t<T>::type;
template <class T>
int binsearch(const std::vector<T> &ts, block_deduction<bool (*)(T)> predicate)
optionally while replacing the function pointer with a std::function<bool(T)>.
You can fix it at the call site.
You can pass T manually binsearch<T>(vec, [](int x){return x<0;}).
You can decay the lambda to a function pointer with putting a + in front of it +[](int x) ... or a static_cast<bool(*)(int)>( ... ).
The best option is Predicate one. This is also what standard library code does.
We can also go a step further and make your code even more generic:
template <class Range, class Predicate>
auto binsearch(const Range &ts, Predicate&& predicate)
-> typename std::decay< decltype(*std::begin(ts)) >::type
The -> typename std::decay ... trailing return type part can be eliminated in C++14.
A benefit to this is that if the body also uses std::begin and std::end to find begin/end iterators, binsearch now supports deques, flat C-style arrays, std::arrays, std::strings, std::vectors, and even some custom types.
If you have any control over binsearch I suggest you refactor it:
template <typename T, typename Predicate>
int binsearch(std::vector<T> const& vec, Predicate&& pred) {
// This is just to illustrate how to call pred
for (auto const& el : vec) {
if (pred(el)) {
// Do something
}
}
return 0; // Or anything meaningful
}
Another way for you is to perform type-erasure on your functor objects/function pointers/whatever... by embedding them into an std::function<bool(T const&)>. To do so, just rewrite the function above as:
template <typename T>
int binsearch(std::vector<T> const& vec, std::function<bool(T const&)> pred);
But since template argument deduction does not do any conversion, you need to explicitly feed your function like the following:
auto my_predicate = [](int x) { return true; }; // Replace with actual predicate
std::vector<int> my_vector = {1, 2, 3, 4};
binsearch(my_vector, std::function<bool (int const&)>(my_predicate));
However given the description of your function, it seems it does the same job as std::find_if.
std::vector<int> my_vector = {1, 12, 15, 13, 16};
auto it = std::find_if(std::begin(my_vector), std::end(my_vector),
[](int vec_el) { return !vec_el%5; });
// it holds the first element in my_vector that is a multiple of 5.
if (it != std::end(my_vector)) {
std::cout << *it << std::endl; // prints 15 in this case
}
Note that to do a binary search, you need more than just a predicate: you need a predicate that defines an order on your range AND a target value.
A function pointer and a lambda function is not the same thing.
An object t can not be assigned to predicate where:
bool (*predicate)(int)
and
auto t = [](int e) -> bool { return e >= 5; });
Might as well use std::function<bool(int)>. Your signature will look like this:
template <typename T>
int binsearch(const std::vector<T> &ts, std::function<bool(T)> predicate){
// ...
}
Now that's not a function pointer, You'll need to bind your founction pointers if they are necessary, (I assume you're fine with just lambdas)
Related
Take the following code, which is a simplified example:
template <typename F>
void foo(F f) {
//bool some = is_variadic_v<F>; // Scenario #1
bool some = true; // Scenario #2
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
A function takes a generic variadic lambda and calls it with a fixed set of arguments. This lambda itself then just calls another function/lambda with a matching prototype.
As one could expect, in scenario 2, when f is called inside foo, the compiler will deduce params... to be the parameter pack {1, 1}.
For scenario #1, I am using a code from another Q&A to deduce the arity of a callable object. If however such an object is callable with more than a pre-defined maximum amount of arguments, it is considered "variadic". In detail, is_variadic_v will employ a form of expression SFINAE where it is attempted to call the function object with a decreasing number of arguments having an "arbitrary type" that is implictly convertible to anything.
The problem is now that apparently, the compiler will deduce F (and along its argument pack) during this metacode, and if it is variadic (such as in this case), it deduces F as a lambda taking the dummy arguments, i.e. something like main()::lambda(<arbitrary_type<0>, arbitrary_type<1>, arbitrary_type<2>, ..., arbitrary_type<N>>) if N is the "variadic limit" from above. Now params... is deduced as arbitrary_type<1>, arbitrary_type<2>, ... and correspondingly, the call some(params...) will fail.
This behaviour can be demonstrated in this little code example:
#include <utility>
#include <type_traits>
#include <iostream>
constexpr int max_arity = 12; // if a function takes more arguments than that, it will be considered variadic
struct variadic_type { };
// it is templated, to be able to create a
// "sequence" of arbitrary_t's of given size and
// hence, to 'simulate' an arbitrary function signature.
template <auto>
struct arbitrary_type {
// this type casts implicitly to anything,
// thus, it can represent an arbitrary type.
template <typename T>
operator T&&();
template <typename T>
operator T&();
};
template <
typename F, auto ...Ints,
typename = decltype(std::declval<F>()(arbitrary_type<Ints>{ }...))
>
constexpr auto test_signature(std::index_sequence<Ints...> s) {
return std::integral_constant<int, size(s)>{ };
}
template <auto I, typename F>
constexpr auto arity_impl(int) -> decltype(test_signature<F>(std::make_index_sequence<I>{ })) {
return { };
}
template <auto I, typename F, typename = std::enable_if_t<(I > 0)>>
constexpr auto arity_impl(...) {
// try the int overload which will only work,
// if F takes I-1 arguments. Otherwise this
// overload will be selected and we'll try it
// with one element less.
return arity_impl<I - 1, F>(0);
}
template <typename F, auto MaxArity>
constexpr auto arity_impl() {
// start checking function signatures with max_arity + 1 elements
constexpr auto tmp = arity_impl<MaxArity+1, F>(0);
if constexpr (tmp == MaxArity+1)
return variadic_type{ }; // if that works, F is considered variadic
else return tmp; // if not, tmp will be the correct arity of F
}
template <typename F, auto MaxArity = max_arity>
constexpr auto arity(F&&) { return arity_impl<std::decay_t<F>, MaxArity>(); }
template <typename F, auto MaxArity = max_arity>
constexpr auto arity_v = arity_impl<std::decay_t<F>, MaxArity>();
template <typename F, auto MaxArity = max_arity>
constexpr bool is_variadic_v = std::is_same_v<std::decay_t<decltype(arity_v<F, MaxArity>)>, variadic_type>;
template <typename F>
void foo(F f) {
bool some = is_variadic_v<F>;
//bool some = true;
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
Can I prevent this behaviour? Can I force the compiler to re-deduce the parameter list?
EDIT:
An additional peculiarity is that the compiler seems to act kind of schizophrenic. When I change the contents of foo to
foo([&some](auto... params) {
// int foo = std::index_sequence<sizeof...(params)>{ };
std::cout << sizeof...(params) << '\n';
});
the compiler will create a program that will print 2 in this example. If however I include the commented line (which, as it makes no sense, should trigger a compiler diagnostic), I get confronted with
error: cannot convert 'std::index_sequence<13>' {aka 'std::integer_sequence<long unsigned int, 13>'} to 'int' in initialization
85 | int foo = std::index_sequence<sizeof...(params)>{ };
so does the compiler now deduces sizeof...(params) to be 2 and 13 at the same time? Or did he change his mind and chooses now 13 just because I added another statement into the lambda? Compilation will also fail if I instead choose a static_assert(2 == sizeof...(params));. So the compiler deduces sizeof...(params) == 2, except if I ask him whether he did deduce 2, because then he didn't.
Apparently, it is very decisive for the parameter pack deduction what is written inside the lambda. Is it just me or does this behaviour really look pathologic?
When I use have template function which accepts another function as a parameter, C++ can't derive template parameters. It's very annoying to specify them all the time. How can I define the following function such that I don't have to specify type parameters every time?
#include <functional>
template <typename S, typename T>
T apply(const S& source, const function<T (const S&)>& f) {
return f(source);
}
template <typename S, class Functor, typename T>
T applyFun(const S& source, const Functor& f) {
return f(source);
}
int main() {
// Can't derive T. Why?
apply(1, [](int x) { return x + 1; });
// Compiles
apply<int, int>(1, [](const int& x) { return x + 1; });
// Can't derive T. Kind of expected.
applyFun(1, [](int x) { return x + 1; });
}
It makes sense to me why it can't derive type parameter in the second function, but not in the first one (since x + 1 is int, so it should deduce that T = int).
A template parameter must appear in a function parameter type to be deductible. Moreover lambdas are not functions so, whatsoever the return type of a lambda cannot participate to template argument deduction.
But in this case, there is no need to specify the return type. Return type deduction can do the job:
template <typename S, class Functor>
auto applyFun(const S& source, const Functor& f) {
return f(source);
}
If you can use C++17, you can use the deduction guides for std::function as follows
template <typename S, typename F,
typename T = typename decltype( std::function{std::declval<F>()} )::result_type>
T applyFun (S const & source, F const & f)
{
return f(source);
}
but, as pointed by Oliv, for your example function there is non need of T because you can use auto (from C++14; auto ... -> decltype(f(source)) in C++11).
-- EDIT --
The OP say
The good thing about this solution is that I can use T inside the function (e.g. if I want to implement vector_map).
You can detect and use T, also inside the function, using a using
Something as
template <typename S, typename F>
auto applyFun (S const & source, F const & f)
{
using T = typename decltype( std::function{f} )::result_type;
return f(source);
}
or simpler: using T = decltype( f(source) );.
The OP also observe that
The downside is that for some reason now I can't write [] (const auto& x) { ... } in function call.
Correct.
Because std::function template types can't be deduced from a generic-lambda.
But using the fact that you know the type of the argument, you can use decltype() again
template <typename S, typename F,
typename T = decltype(std::declval<F const>()(std::declval<S const>()))>
T applyFun (S const & source, F const & f)
{ return f(source); }
This solution should works also for C++14 and C++11.
I try to pass to a variadic template function a list of references and pass it to another function. The code that I wrote is the following:
template <typename T>
void fun(cv::Point_<T> & pt) { pt.x++; pt.y++; }
template <class ... args>
void caller(args & ... list) {
typedef typename std::tuple_element<0, std::tuple<args...> >::type T;
std::array<std::reference_wrapper<T>, sizeof...(list)> values {list ... };
for(int i=0; i<values.size(); i++)
fun(values[i]);
}
then I call the function caller in this way:
cv::Point2f a, b, c;
caller(a, b, c);
the compiler give me the following error:
No matching function for call to 'fun'
Candidate template ignored: could not match 'Point_' against 'reference_wrapper'
what I missing?
Although std::reference_wrapper<T> has an implicit conversion to T&, you cannot use both an implicit conversion and template argument deduction at the same time, and template argument deduction is necessary to call fun.
Try
fun(values[i].get());
Even simpler is
template <typename...Args>
void caller(Args&...args)
{
auto tmp = { (func(args),0)..., 0 };
}
This uses the fact that parameter pack expansion can occur in braced init lists. Since func() returns void, we cannot simply use { func(args)... }, but use (func(args),0) to have an int. Finally, the last 0 is to ensure that the code compiles (and does nothing) in case of an empty parameter pack.
You can generalise this and write a template that calls a given generic function for every element of a pack:
template <typename Func, typename...Args>
void call_for_each(Func &&func, Args&&...args)
{
auto unused = { (func(std::forward<Args>(args)),0)...,0 };
}
which may be used like this (C++14)
int main()
{
int a=1;
double b=2.4;
auto func = [](auto&x) { std::cout<<' '<<x++; };
call_for_each(func,a,b);
std::cout<<'\n';
call_for_each(func,a,b);
std::cout<<'\n';
}
This uses a C++14 lambda (taking an auto argument). Note that the parameter pack must come last among the template parameters of call_for_each.
Since the goal of this might be to iterate over all args, here's a more generic solution. We are going to implement for_pack:
template<typename... Args, typename F>
void for_pack(F function, Args&&... args) {
using expand = int[];
(void)expand{(function(std::forward<Args>(args)), void(), 0)..., 0};
}
This will execute function for every args in Args.
Now, your function caller is much more trivial to implement:
template <typename... args>
void caller(args&... list) {
for_pack([&](cv::Point_<T>& arg){
fun(arg);
}, list...);
}
Since a google search for "c++ pass reference parameters to variadic template" gives this as first result, I'll put this generic solution here.
struct HH { /*...*/ void change_me() { /*...*/ } };
template<typename...T> void parms_r_refs() {}
template<typename H, typename...T> void parms_r_refs(H &h, T&...t) { h.change_me(); parms_r_refs(t...); }
template<typename...T> void parms_r_refs(T&...t) { parms_r_refs(t...); }
HH a, b, c;
..
parms_r_refs(a, b, c);
..
I'm learning C++, and I'm trying to implement a binary search function that finds the first element for which a predicate holds. The function's first argument is a vector and the second argument is a function that evaluates the predicate for a given element. The binary search function looks like this:
template <typename T> int binsearch(const std::vector<T> &ts, bool (*predicate)(T)) {
...
}
This works as expected if used like this:
bool gte(int x) {
return x >= 5;
}
int main(int argc, char** argv) {
std::vector<int> a = {1, 2, 3};
binsearch(a, gte);
return 0;
}
But if I use a lambda function as a predicate, I get a compiler error:
search-for-a-range.cpp:20:5: error: no matching function for call to 'binsearch'
binsearch(a, [](int e) -> bool { return e >= 5; });
^~~~~~~~~
search-for-a-range.cpp:6:27: note: candidate template ignored: could not match 'bool (*)(T)' against '(lambda at
search-for-a-range.cpp:20:18)'
template <typename T> int binsearch(const std::vector<T> &ts,
^
1 error generated.
The above error is generated by
binsearch(a, [](int e) -> bool { return e >= 5; });
What's wrong? Why is the compiler not convinced that my lambda has the right type?
Your function binsearch takes a function pointer as argument. A lambda and a function pointer are different types: a lambda may be considered as an instance of a struct implementing operator().
Note that stateless lambdas (lambdas that don't capture any variable) are implicitly convertible to function pointer. Here the implicit conversion doesn't work because of template substitution:
#include <iostream>
template <typename T>
void call_predicate(const T& v, void (*predicate)(T)) {
std::cout << "template" << std::endl;
predicate(v);
}
void call_predicate(const int& v, void (*predicate)(int)) {
std::cout << "overload" << std::endl;
predicate(v);
}
void foo(double v) {
std::cout << v << std::endl;
}
int main() {
// compiles and calls template function
call_predicate(42.0, foo);
// compiles and calls overload with implicit conversion
call_predicate(42, [](int v){std::cout << v << std::endl;});
// doesn't compile because template substitution fails
//call_predicate(42.0, [](double v){std::cout << v << std::endl;});
// compiles and calls template function through explicit instantiation
call_predicate<double>(42.0, [](double v){std::cout << v << std::endl;});
}
You should make your function binsearch more generic, something like:
template <typename T, typename Predicate>
T binsearch(const std::vector<T> &ts, Predicate p) {
// usage
for(auto& t : ts)
{
if(p(t)) return t;
}
// default value if p always returned false
return T{};
}
Take inspiration from standard algorithms library.
The lambda expression with empty capture list could be implicitly converted to a function pointer. But the function pointer predicate is taking T as its parameter, that need to be deduced. Type conversion won't be considered in template type deduction, T can't be deduced; just as the error message said, candidate template (i.e. binsearch) is ignored.
You can use operator+ to achieve this, it'll convert lambda to function pointer, which will be passed to binsearch later and then T will be successfully deduced[1].
binsearch(a, +[](int e) -> bool { return e >= 5; });
// ~
Of course you could use static_cast explicitly:
binsearch(a, static_cast<bool(*)(int)>([](int e) -> bool { return e >= 5; }));
Note if you change predicate's type to be independent of T, i.e. bool (*predicate)(int), passing lambda with empty capture list would work too; the lambda expression will be converted to function pointer implicitly.
Another solution is changing the parameter type from function pointer to std::function, which is more general for functors:
template <typename T> int binsearch(const std::vector<T> &ts, std::function<bool (typename std::vector<T>::value_type)> predicate) {
...
}
then
binsearch(a, [](int e) -> bool { return e >= 5; });
[1] A positive lambda: '+[]{}' - What sorcery is this?
Why is the compiler not convinced that my lambda has the right type?
Template functions being told to deduce their template parameters do no conversion. A lambda is not a function pointer, so there is no way to deduce the T in that argument. As all function arguments independently deduce their template parameters (unless deduction is blocked), this results in an error.
There are a number of fixes you can do.
You can fix the template function.
template <class T>
int binsearch(const std::vector<T> &ts, bool (*predicate)(T))
Replace the function pointer with a Predicate predicate or Predicate&& predicate and leave the body unchanged.
template <class T, class Predicate>
int binsearch(const std::vector<T> &ts, Predicate&& predicate)
Use deduction blocking:
template<class T>struct tag_t{using type=T;};
template<class T>using block_deduction=typename tag_t<T>::type;
template <class T>
int binsearch(const std::vector<T> &ts, block_deduction<bool (*)(T)> predicate)
optionally while replacing the function pointer with a std::function<bool(T)>.
You can fix it at the call site.
You can pass T manually binsearch<T>(vec, [](int x){return x<0;}).
You can decay the lambda to a function pointer with putting a + in front of it +[](int x) ... or a static_cast<bool(*)(int)>( ... ).
The best option is Predicate one. This is also what standard library code does.
We can also go a step further and make your code even more generic:
template <class Range, class Predicate>
auto binsearch(const Range &ts, Predicate&& predicate)
-> typename std::decay< decltype(*std::begin(ts)) >::type
The -> typename std::decay ... trailing return type part can be eliminated in C++14.
A benefit to this is that if the body also uses std::begin and std::end to find begin/end iterators, binsearch now supports deques, flat C-style arrays, std::arrays, std::strings, std::vectors, and even some custom types.
If you have any control over binsearch I suggest you refactor it:
template <typename T, typename Predicate>
int binsearch(std::vector<T> const& vec, Predicate&& pred) {
// This is just to illustrate how to call pred
for (auto const& el : vec) {
if (pred(el)) {
// Do something
}
}
return 0; // Or anything meaningful
}
Another way for you is to perform type-erasure on your functor objects/function pointers/whatever... by embedding them into an std::function<bool(T const&)>. To do so, just rewrite the function above as:
template <typename T>
int binsearch(std::vector<T> const& vec, std::function<bool(T const&)> pred);
But since template argument deduction does not do any conversion, you need to explicitly feed your function like the following:
auto my_predicate = [](int x) { return true; }; // Replace with actual predicate
std::vector<int> my_vector = {1, 2, 3, 4};
binsearch(my_vector, std::function<bool (int const&)>(my_predicate));
However given the description of your function, it seems it does the same job as std::find_if.
std::vector<int> my_vector = {1, 12, 15, 13, 16};
auto it = std::find_if(std::begin(my_vector), std::end(my_vector),
[](int vec_el) { return !vec_el%5; });
// it holds the first element in my_vector that is a multiple of 5.
if (it != std::end(my_vector)) {
std::cout << *it << std::endl; // prints 15 in this case
}
Note that to do a binary search, you need more than just a predicate: you need a predicate that defines an order on your range AND a target value.
A function pointer and a lambda function is not the same thing.
An object t can not be assigned to predicate where:
bool (*predicate)(int)
and
auto t = [](int e) -> bool { return e >= 5; });
Might as well use std::function<bool(int)>. Your signature will look like this:
template <typename T>
int binsearch(const std::vector<T> &ts, std::function<bool(T)> predicate){
// ...
}
Now that's not a function pointer, You'll need to bind your founction pointers if they are necessary, (I assume you're fine with just lambdas)
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?
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.
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) {
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.