La chiamata a lambda è ambigua nonostante affermi esplicitamente il tipo restituito


11

Una funzione sovraccarica dovrebbe includere entrambi i funzione, dato che il tipo di lambda è decidibile (calcolabile su un std::function(per favore correggimi se sbaglio). La domanda è: perché c'è un errore di compilazione sotto, nonostante il tipo lambda sia esplicitamente definito? ( [&]() -> Type {})

Si noti che per la mia soluzione attuale ho bisogno dell'acquisizione per riferimento, ecco perché il codice contiene la logica per esso.

L'esempio seguente descrive il problema:

#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
Perché std::function<void(int)>può essere costruito anche da un lambda che restituisce qualcosa (che fa ignorare il valore restituito).
HolyBlackCat

1
A parte questo, specificare esplicitamente il tipo di ritorno di quel lambda non fa esattamente nulla.
Deduplicatore,

Risposte:


8

Perché la seconda espressione lambda che ritorna boolpotrebbe convertirsi in entrambi std::function<void(int)>e std::function<bool(int)>implicitamente.

std::function ha un costruttore di conversione:

template< class F >
function( F f );

Questo costruttore non partecipa alla risoluzione del sovraccarico a meno che f non sia richiamabile per i tipi di argomento Args ... e restituisce il tipo R. (dal C ++ 14)

Come la definizione di Callable ,

Le seguenti espressioni devono essere valide:

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

dove INVOKE (f, t1, t2, ..., tN) è definito come static_cast<void>(INVOKE(f, t1, t2, ..., tN))se R è possibilmente qualificato come cv void, altrimenti INVOKE (f, t1, t2, ..., tN) , implicitamente convertito in R

Si noti che il 2 ° lambda che ritorna bool, per std::function<void(int)>, come mostrato sopra, static_cast<void>(INVOKE(f, t1, t2, ..., tN))è un'espressione valida (il reso boolviene appena convertito in void). Quindi potrebbe anche convertirsi in modo std::function<void(int)>implicito e causare il problema di ambiguità.


6

È possibile esplicitamente static_castlambda al tipo corretto

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

Oppure memorizza il lambda nel std::function<bool(int)>tipo corretto e passa alla funzione (se do_some(lmda)deve essere chiamato più volte)

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

O come ha suggerito @MaxLanghof semplicemente costruire std::function<bool(int)>da lambda in movimento

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

Puoi saltare il static_caste semplicemente costruirne uno std::functiondirettamente da esso. Questo è tutto ciò che accade durante la conversione implicita comunque.
Max Langhof,

Il mio punto è che puoi letteralmente rimuovere l' static_cast<ultimo >e l' ultimo e farà la stessa cosa ma con meno battiture. Non ha bisogno di più linee o altro. godbolt.org/z/fQTqF4
Max Langhof,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.