So, I have a method and its overload for a class, written like this:
bool my_for_each(someIterator begin, someIterator end, bool (*_lambda)(someIterator));
void my_for_each(someIterator begin, someIterator end, void (*_lambda)(someIterator));
As you can see, the only difference is the signature of a passed function, that is, its return type. Although the code above works perfectly fine when i call it:
my_for_each(iteratorBegin, iteratorEnd, [](someIterator) {; }); // calls void version
my_for_each(iteratorBegin, iteratorEnd, [](someIterator)->bool {return false; }); // calls bool version
..., if I write the my_for_each function as follows:
bool my_for_each(someIterator begin, someIterator end, std::function<bool(someIterator)> _lambda);
void my_for_each(someIterator begin, someIterator end, std::function<void(someIterator)> _lambda);
The code fails to compile if i call the function the same way (C2668 ambiguous call to overloaded function). Although, if I manually cast the function:
my_for_each(iteratorBegin, iteratorEnd, static_cast<std::function<void(someIterator)>>([](someIterator) {; })); //calls void version
my_for_each(iteratorBegin, iteratorEnd, static_cast<std::function<bool(someIterator)>>([](someIterator) -> bool { return false; })); //calls bool version
The code works perfectly fine. So I'm just wondering:
The compiler is VS2015.
Thanks and have a nice day!
std::function
and lambdas have distinct types, meaning the 2nd example requires an implicit conversion from the lambda's type to std::function
.
The problem is that the lambda that returns bool
can be implicitly converted to both std::function<bool(T)>
and std::function<void(T)>
, thus both overloads are equally valid choices for your call, leading to an ambiguity error. When you manually cast them to the proper std::function
the ambiguity is explicitly resolved.
Edit : Workaround
You can solve this problem by providing an addition templated overload that accepts any kind of callable type, deduces the return type for that callable type and preforms the cast automatically. I've changed someIterator
to a template argument for generality. Since you haven't provided implementation details for your my_for_each
functions, I have omitted those implementations. Since it appears that you only want to support void
and bool
return types, I've added a static_assert
to generate a clear compiler error in case unsupported return types are provided.
#include <functional>
#include <type_traits>
// Implement for std::function<bool(iter)>
template<class I>
bool my_for_each(I begin, I end, std::function<bool(I)> lambda);
// Implement for std::function<void(iter)>
template<class I>
void my_for_each(I begin, I end, std::function<void(I)> lambda);
// Dispatch to the right overload
template<class T, class I>
auto my_for_each(I begin, I end, T&& lambda)
{
using return_type = decltype(lambda(begin)); // Obtain the return type of lambda
static_assert(std::is_same<return_type, bool>::value || std::is_same<return_type, void>::value,
"my_for_each only accepts function objects that return void or bool");
using function_type = std::function<return_type(I)>; // The type to cast lambda to
return my_for_each(begin, end, function_type(std::forward<T>(lambda))); // Preform the cast
}