Виклик лямбда неоднозначний, незважаючи на те, що прямо вказано тип повернення


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
Як осторонь, чітко вказавши тип повернення цієї лямбда, нічого не робить.
Deduplicator

Відповіді:


8

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

std::function має конструктор, що перетворює:

template< class F >
function( F f );

Цей конструктор не бере участі в дозволі перевантаження , якщо F не є відкличним для типів аргументів Арга ... і типу значення, що повертається 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

Ви можете явно 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;
   }
});

Ви можете пропустити static_castі просто сконструювати std::functionбезпосередньо з нього. Це все, що відбувається під час неявної конверсії.
Макс Ленгоф

Моя думка полягає в тому, що ви можете буквально просто видалити static_cast<і останнє, >і це зробить те саме, але з меншим набором тексту. Для цього не потрібно більше рядків чи нічого. godbolt.org/z/fQTqF4
Макс Лангхоф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.