Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;
      }
   );
}
like image 688
Dávid Tóth Avatar asked Oct 30 '19 14:10

Dávid Tóth


2 Answers

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.

like image 121
songyuanyao Avatar answered Oct 27 '22 12:10

songyuanyao


You can explicitly static_cast the lambda to the proper type

using FunBoolRet = std::function<bool(int)>;

do_some(static_cast<FunBoolRet >([&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }));

Or store the lambda to the proper std::function<bool(int)> type and pass to the function(if do_some(lmda) should be called many times)

FunBoolRet lmda = [&](int in)
{
    local_to_be_modified += in;
    std::cout << "This is bool-" << std::endl;
    return true;
};    
do_some(lmda); // pass the lambda

Or as @MaxLanghof suggested simply construct std::function<bool(int)> from lambda on the go

do_some(FunBoolRet{
   [&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }
});
like image 20
JeJo Avatar answered Oct 27 '22 14:10

JeJo