Search code examples
c++c++11lambdaimplicit-conversionstd-function

Call to lambda is ambiguous despite explicitly stating the return type


An overloaded function should take both functors in, given the type of the lambda is decidable ( castable to an std::function (please correct me if I'm wrong ). The question is: Why is there a compile error below, despite the lambda type being explicitly defined? ( [&]() -> Type {} )

Please note, that for my current solution I need the capture-by-reference, that is why the code contains the logic for it.

The following example describes the problem:

#include <iostream>
#include <string>    
#include <functional>

void do_some(std::function<void(int)> thing) 
{
   thing(5);
}

void do_some(std::function<bool(int)> thing)
{
   if (thing(10)) 
   {
      std::cout << "it's true!" << std::endl;
   }
}

int main()
{
   int local_to_be_modified = 0;
   do_some(
      [&](int in)
      {
         local_to_be_modified = in;
         std::cout << "This is void-" << std::endl;
      }
   );
   do_some(
      [&](int in) -> bool
      { 
         // error: call to 'do_some' is ambiguous
         local_to_be_modified += in;
         std::cout << "This is bool-" << std::endl;
         return true;
      }
   );
}

Solution

  • Because the 2nd lambda expression returning bool could convert to both std::function<void(int)> and std::function<bool(int)> implicitly.

    std::function has a converting constructor:

    template< class F >
    function( F f );
    

    This constructor does not participate in overload resolution unless f is Callable for argument types Args... and return type R. (since C++14)

    As the definition of Callable,

    The following expressions must be valid:

    INVOKE<R>(f, std::declval<ArgTypes>()...)
    

    where INVOKE(f, t1, t2, ..., tN) is defined as static_cast<void>(INVOKE(f, t1, t2, ..., tN)) if R is possibly cv-qualified void, otherwise INVOKE(f, t1, t2, ..., tN), implicitly converted to R

    Note that the 2nd lambda returning bool, for the std::function<void(int)>, as shown above, static_cast<void>(INVOKE(f, t1, t2, ..., tN)) is a valid expression (the returned bool is just converted to void). Then it could also convert to std::function<void(int)> implicitly and causes the ambiguity issue.