Вызов лямбда неоднозначен, несмотря на явное указание типа возврата

11

Перегруженная функция должна принимать оба функтора, учитывая, что тип лямбды можно разрешить (можно преобразовать в 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;
      }
   );
}
Дэвид Тот
источник
6
Потому что std::function<void(int)>может быть построен даже из лямбды, которая что-то возвращает (что приводит к игнорированию возвращаемого значения).
HolyBlackCat
1
Кроме того, явное указание типа возврата этой лямбды ничего не делает.
дедупликатор

Ответы:

8

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

std::function имеет конвертирующий конструктор:

template< class F >
function( F f );

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

Как определение Callable ,

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

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)>неявно и вызывает проблему неоднозначности.

songyuanyao
источник
6

Вы можете явно 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;
   }
});
JeJo
источник
Вы можете пропустить static_castи просто построить std::functionпрямо из него. Это все, что происходит во время неявного преобразования.
Макс Лангхоф
Я хочу сказать, что вы можете буквально убрать static_cast<и последний, >и он будет делать то же самое, но с меньшим набором текста. Для этого не нужно больше строк или чего-либо еще. godbolt.org/z/fQTqF4
Макс