Вызов лямбда неоднозначен, несмотря на явное указание типа возврата - PullRequest
10 голосов
/ 30 октября 2019

Перегруженная функция должна принимать оба функтора, учитывая, что лямбда-тип является разрешимым (с возможностью преобразования в std::function (пожалуйста, исправьте меня, если я ошибаюсь). Вопрос в следующем: почему ниже ошибка компиляции,несмотря на то, что лямбда-тип явно задан? ([&]() -> Type {})

Обратите внимание, что для моего текущего решения мне нужен захват по ссылке, поэтому код содержит логику для него.

Следующий пример описывает проблему:

#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;
      }
   );
}

Ответы [ 2 ]

7 голосов
/ 30 октября 2019

Поскольку 2-е лямбда-выражение, возвращающее bool, может неявно преобразовываться в std::function<void(int)> и std::function<bool(int)>.

std::function имеет конструктор преобразования:

template< class F >
function( F f );

Этот конструктор не участвует в разрешении перегрузки, если f не является Вызываемым для типов аргументов Args ... и возвращаемого типа R. (начиная с C ++ 14)

Как определение Вызываемый ,

Следующие выражения должны быть действительными:

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

, где INVOKE (f, t1, t2, ..., tN) определяется как static_cast<void>(INVOKE(f, t1, t2, ..., tN)), если R возможно cv-квалифицирован void, в противном случае INVOKE (f, t1, t2, ...,tN) , неявно преобразуется в R

Обратите внимание, что 2-я лямбда, возвращающая bool, для std::function<void(int)>, как показано выше, static_cast<void>(INVOKE(f, t1, t2, ..., tN)) является допустимым выражением (возвращаемое bool просто конвертируется в void). Тогда он также может неявно преобразоваться в std::function<void(int)> и вызвать проблему неоднозначности.

6 голосов
/ 30 октября 2019

Вы можете явно static_cast лямбда для правильного типа

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;
   }));

Или сохранить лямбда в правильный тип std::function<bool(int)> и перейти к функции (если do_some(lmda) должен вызываться много раз)

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

Или как @ MaxLanghof предложил просто построить std::function<bool(int)> из лямбды на ходу

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