Der Aufruf von Lambda ist trotz expliziter Angabe des Rückgabetyps nicht eindeutig

11

Eine überladene Funktion sollte beide Funktoren aufnehmen, da der Lambda-Typ entscheidbar ist (umsetzbar auf einen std::function(bitte korrigieren Sie mich, wenn ich falsch liege). Die Frage ist: Warum gibt es unten einen Kompilierungsfehler, obwohl der Lambda-Typ explizit ist definiert? ( [&]() -> Type {})

Bitte beachten Sie, dass ich für meine aktuelle Lösung die Erfassung als Referenz benötige. Deshalb enthält der Code die Logik dafür.

Das folgende Beispiel beschreibt das 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;
      }
   );
}
David Tóth
quelle
6
Denn std::function<void(int)>kann sogar aus einem Lambda konstruiert werden, das etwas zurückgibt (wodurch der Rückgabewert ignoriert wird).
HolyBlackCat
1
Abgesehen davon bewirkt die explizite Angabe des Rückgabetyps dieses Lambda genau nichts.
Deduplikator

Antworten:

8

Weil der 2. Lambda-Ausdruck, der zurückkehrt bool, in beide std::function<void(int)>und std::function<bool(int)>implizit konvertiert werden könnte .

std::function hat einen konvertierenden Konstruktor:

template< class F >
function( F f );

Dieser Konstruktor beteiligt sich nicht an der Überladungsauflösung , es sei denn f ist aufrufbare für Argumenttypen Args ... und Rückgabetyp R. (da C ++ 14)

Wie die Definition von Callable ,

Die folgenden Ausdrücke müssen gültig sein:

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

wobei INVOKE (f, t1, t2, ..., tN) so definiert ist, als static_cast<void>(INVOKE(f, t1, t2, ..., tN))ob R möglicherweise cv-qualifiziert ist void, andernfalls INVOKE (f, t1, t2, ..., tN) implizit in R konvertiert wird

Beachten Sie, dass das 2. Lambda bool, das für das zurückgegeben wird std::function<void(int)>, wie oben gezeigt, static_cast<void>(INVOKE(f, t1, t2, ..., tN))ein gültiger Ausdruck ist (das zurückgegebene boolwird nur in konvertiert void). Dann könnte es auch std::function<void(int)>implizit in konvertieren und das Mehrdeutigkeitsproblem verursachen.

songyuanyao
quelle
6

Sie können static_castdas Lambda explizit auf den richtigen Typ einstellen

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

Oder speichern Sie das Lambda im richtigen std::function<bool(int)>Typ und übergeben Sie es an die Funktion (falls do_some(lmda)dies mehrmals aufgerufen werden sollte).

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

Oder wie @MaxLanghof vorschlug, einfach unterwegsstd::function<bool(int)> aus Lambda zu konstruieren

do_some(FunBoolRet{
   [&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }
});
JeJo
quelle
Sie können das überspringen static_castund einfach ein std::functiondirekt daraus erstellen. Das ist sowieso alles, was während der impliziten Konvertierung passiert.
Max Langhof
Mein Punkt ist, dass Sie buchstäblich nur das static_cast<und das letzte entfernen können >und es das gleiche tun wird, aber mit weniger Eingabe. Es braucht keine weiteren Zeilen oder so. godbolt.org/z/fQTqF4
Max Langhof