Wie deklariere ich eine Funktion, die ein Lambda akzeptiert?

81

Ich habe im Internet viele Tutorials gelesen, in denen erklärt wurde, wie man Lambdas mit der Standardbibliothek verwendet (z. B. std::find), und alle waren sehr interessant, aber ich konnte keine finden, die erklärten, wie ich ein Lambda für meine eigenen Funktionen verwenden kann.

Beispielsweise:

int main()
{
    int test = 5;
    LambdaTest([&](int a) { test += a; });

    return EXIT_SUCCESS;
}

Wie soll ich deklarieren LambdaTest? Was ist die Art des ersten Arguments? Und wie kann ich dann die an sie übergebene anonyme Funktion - zum Beispiel - "10" als Argument aufrufen?

Thomas Bonini
quelle

Antworten:

77

Da Sie neben Lambdas wahrscheinlich auch Funktionszeiger und Funktionsobjekte akzeptieren möchten, möchten Sie wahrscheinlich Vorlagen verwenden, um Argumente mit einem zu akzeptieren operator(). Dies ist, was die Standardfunktionen wie find tun. Es würde so aussehen:

template<typename Func>
void LambdaTest(Func f) {
    f(10);
}

Beachten Sie, dass diese Definition keine c ++ 0x-Funktionen verwendet und daher vollständig abwärtskompatibel ist. Nur der Aufruf der Funktion mit Lambda-Ausdrücken ist c ++ 0x-spezifisch.

sepp2k
quelle
2
Im Fehlerfall sind die Fehlermeldungen schwer zu verstehen.
Liori
13
Es kommt darauf an, ob es das Beste ist. Dies verwendet eine Vorlage und die andere nicht. Dies bedeutet, dass die Funktion nicht mehr virtuell sein und nicht separat in der CPP-Datei definiert werden kann. std::functionist auch in der Lage, Funktionsobjektklassentypen zu übernehmen, ist jedoch beim Aufrufen etwas langsamer. Aber dieser Unterschied ist für die meisten Anwendungen vernachlässigbar :)
Johannes Schaub - litb
2
"am besten" liegt im Auge des Betrachters :-) Diese Antwort verwendet eine, functordie nett ist, aber nicht wirklich die ursprüngliche Frage beantwortet (und was mich hierher geführt hat), die lautete: "Wie verwende ich ein Lambda für meine eigenen Funktionen?" . Außerdem haben Vorlagen ihre eigenen Probleme, mit denen die Antwort std::functionnicht übereinstimmt.
Marco Massenzio
1
@Marco Für diese Antwort müssen Sie keine Funktionen verwenden, sondern können alles verwenden, was Sie möchten - einschließlich Lambdas.
sepp2k
67

Wenn Sie nicht alles vorlegen möchten, können Sie Folgendes tun:

void LambdaTest (const std::function <void (int)>& f)
{
    ...
}
doublep
quelle
1
Mit dieser Syntax kann ich die Funktionsvariable speichern, um sie später aufzurufen, oder? Zum Beispiel wollte ich eine Funktion implementieren, die es erlaubt, asynchrone Datenbankabfragen auszuführen, wobei das Lambda als Rückruf fungiert. (Natürlich würde ich nicht in der Lage sein, auf Schließungen als Referenz zuzugreifen)
Thomas Bonini
1
Ist es nicht idiomatischer, Funktionen als Wert zu übergeben?
Fredoverflow
1
@Andreas Bonini: Ja, wenn Sie unter std::function(keine Referenz) speichern, erstellen Sie eine Kopie von f. Ich bin mir jedoch nicht sicher, wie Lambda / Closures mit Referenzen umgehen, wenn das Objekt, auf das verwiesen wird, außerhalb des Gültigkeitsbereichs liegt, wahrscheinlich UB. @FredOverflow: Mein Verständnis ist, dass dies std::functionkein triviales Objekt ist, insbesondere beim Einwickeln von Lambdas. Es ist wahrscheinlich besser, darauf zu verweisen, um unnötiges Kopieren zu vermeiden.
Doublep
Wenn Ihr Lambda den Stapel nach Wert erfasst, kann das Lambda diese Variablen überleben und behält weiterhin Kopien davon. Wenn es als Referenz erfasst wird, haben Sie ein Problem.
Kate Gregory
1
Wir müssen es innerhalb der Funktion kopieren, daher macht es keinen Sinn, es zu kopieren, wenn es übergeben wird
Casebash
8

Ich möchte dieses einfache, aber selbsterklärende Beispiel beisteuern. Es zeigt, wie "aufrufbare Dinge" (Funktionen, Funktionsobjekte und Lambdas) an eine Funktion oder an ein Objekt übergeben werden.

// g++ -std=c++11 thisFile.cpp

#include <iostream>
#include <thread>

using namespace std;

// -----------------------------------------------------------------
class Box {
public:
  function<void(string)> theFunction; 
  bool funValid;

  Box () : funValid (false) { }

  void setFun (function<void(string)> f) {
    theFunction = f;
    funValid = true;
  }

  void callIt () {
    if ( ! funValid ) return;
    theFunction (" hello from Box ");
  }
}; // class

// -----------------------------------------------------------------
class FunClass {
public:
  string msg;
  FunClass (string m) :  msg (m) { }
  void operator() (string s) {
    cout << msg <<  s << endl; 
  }
};

// -----------------------------------------------------------------
void f (string s) {
  cout << s << endl;
} // ()

// -----------------------------------------------------------------
void call_it ( void (*pf) (string) ) {
  pf( "call_it: hello");
} // ()

// -----------------------------------------------------------------
void call_it1 ( function<void(string)> pf ) {
  pf( "call_it1: hello");
} // ()

// -----------------------------------------------------------------
int main() {

  int a = 1234;

  FunClass fc ( " christmas ");

  f("hello");

  call_it ( f );

  call_it1 ( f );

  // conversion ERROR: call_it ( [&] (string s) -> void { cout << s << a << endl; } );

  call_it1 ( [&] (string s) -> void { cout << s << a << endl; } );

  Box ca;

  ca.callIt ();

  ca.setFun (f);

  ca.callIt ();

  ca.setFun ( [&] (string s) -> void { cout << s << a << endl; } );

  ca.callIt ();

  ca.setFun (fc);

  ca.callIt ();

} // ()
cibercitizen1
quelle
2
Sie brauchen kein funValid: en.cppreference.com/w/cpp/utility/functional/function/… , sagen Sie einfachif ( ! theFunction )
Erik Aronesty