C++

Lambda-Ausdrücke in C++

Lambda-Ausdrücke in C++

Warum Lambda-Ausdruck?

Betrachten Sie die folgende Aussage:

    int myInt = 52;

Hier ist myInt ein Bezeichner, ein lvalue. 52 ist ein Literal, ein Prvalue. Heute ist es möglich, eine Funktion speziell zu codieren und an die Position von 52 . zu setzen. Eine solche Funktion wird als Lambda-Ausdruck bezeichnet. Betrachten Sie auch das folgende kurze Programm:

#einschließen
Verwenden von Namespace-Std;
int fn (int par)

int-Antwort = Par + 3;
Antwort zurück;

int main()

fn(5);
0 zurückgeben;

Heute ist es möglich, eine Funktion speziell zu codieren und an die Position des Arguments von 5 des Funktionsaufrufs fn(5) zu setzen. Eine solche Funktion wird als Lambda-Ausdruck bezeichnet. Der Lambda-Ausdruck (Funktion) an dieser Position ist ein prvalue.

Jedes Literal außer dem String-Literal ist ein Prvalue. Der Lambda-Ausdruck ist ein spezielles Funktionsdesign, das als Literal in Code passen würde. Es ist eine anonyme (unbenannte) Funktion. In diesem Artikel wird der neue primäre C++-Ausdruck erläutert, der als Lambda-Ausdruck bezeichnet wird. Grundkenntnisse in C++ sind Voraussetzung, um diesen Artikel zu verstehen.

Artikelinhalt

  • Illustration des Lambda-Ausdrucks
  • Teile des Lambda-Ausdrucks
  • Aufnahmen
  • Klassisches Callback-Funktionsschema mit Lambda-Ausdruck
  • Der Trailing-Return-Typ
  • Schließung
  • Fazit

Illustration des Lambda-Ausdrucks

Im folgenden Programm wird einer Variablen eine Funktion, die ein Lambda-Ausdruck ist, zugewiesen:

#einschließen
Verwenden von Namespace-Std;
auto fn = [](int param)

int-Antwort = Param + 3;
Antwort zurück;
;
int main()

Autovariable = fn(2);
cout << variab << '\n';
0 zurückgeben;

Die Ausgabe ist:

    5

Außerhalb der Funktion main() gibt es die Variable fn. Sein Typ ist automatisch. Auto bedeutet in dieser Situation, dass der tatsächliche Typ, z. B. int oder float, durch den rechten Operanden des Zuweisungsoperators (=) bestimmt wird. Rechts vom Zuweisungsoperator steht ein Lambda-Ausdruck. Ein Lambda-Ausdruck ist eine Funktion ohne den vorhergehenden Rückgabetyp. Beachten Sie die Verwendung und Position der eckigen Klammern, []. Die Funktion gibt 5 zurück, ein int, das den Typ für fn . bestimmt.

In der Funktion main() gibt es die Anweisung:

    Autovariable = fn(2);

Das bedeutet, dass fn außerhalb von main() als Bezeichner für eine Funktion endet. Seine impliziten Parameter sind die des Lambda-Ausdrucks. Der Typ für variab ist auto.

Beachten Sie, dass der Lambda-Ausdruck mit einem Semikolon endet, genau wie die Klassen- oder Strukturdefinition mit einem Semikolon endet.

Im folgenden Programm ist eine Funktion, bei der es sich um einen Lambda-Ausdruck handelt, der den Wert 5 zurückgibt, ein Argument für eine andere Funktion:

#einschließen
Verwenden von Namespace-Std;
void otherfn (int no1, int (*ptr)(int))

int no2 = (*ptr)(2);
cout << no1 << " << no2 << '\n';

int main()

otherfn(4, [](int param)

int-Antwort = Param + 3;
Antwort zurück;
);
0 zurückgeben;

Die Ausgabe ist:

    4  5

Hier gibt es zwei Funktionen, den Lambda-Ausdruck und die otherfn()-Funktion. Der Lambda-Ausdruck ist das zweite Argument von otherfn(), aufgerufen in main(). Beachten Sie, dass die Lambda-Funktion (Ausdruck) in diesem Aufruf nicht mit einem Semikolon endet, da es sich hier um ein Argument (keine eigenständige Funktion) handelt.

Der Lambda-Funktionsparameter in der Definition der otherfn()-Funktion ist ein Zeiger auf eine Funktion. Der Zeiger hat den Namen ptr. Der Name ptr wird in der otherfn()-Definition verwendet, um die Lambda-Funktion aufzurufen.

Die Aussage,

    int no2 = (*ptr)(2);

In der otherfn()-Definition ruft es die Lambda-Funktion mit einem Argument von 2 . auf. Der Rückgabewert des Aufrufs, "(*ptr)(2)" aus der Lambda-Funktion, wird no2 zugewiesen.

Das obige Programm zeigt auch, wie die Lambda-Funktion im C++-Callback-Funktionsschema verwendet werden kann.

Teile des Lambda-Ausdrucks

Die Teile einer typischen Lambda-Funktion sind wie folgt:

    [] ()
  • [] ist die Capture-Klausel. Es kann Gegenstände haben.
  • () ist für die Parameterliste.
  • ist für den Funktionskörper. Wenn die Funktion allein steht, sollte sie mit einem Semikolon enden.

Aufnahmen

Die Lambda-Funktionsdefinition kann einer Variablen zugewiesen oder als Argument für einen anderen Funktionsaufruf verwendet werden. Die Definition für einen solchen Funktionsaufruf sollte als Parameter einen Zeiger auf eine Funktion haben, entsprechend der Lambda-Funktionsdefinition.

Die Lambda-Funktionsdefinition unterscheidet sich von der normalen Funktionsdefinition. Sie kann einer Variablen im globalen Gültigkeitsbereich zugewiesen werden; diese funktionszugeordnete-variable kann auch in einer anderen Funktion codiert werden. Wenn er einer globalen Gültigkeitsbereichsvariablen zugewiesen wird, kann sein Körper andere Variablen im globalen Gültigkeitsbereich sehen. Wenn sie einer Variablen innerhalb einer normalen Funktionsdefinition zugewiesen wird, kann ihr Rumpf andere Variablen im Funktionsumfang nur mit Hilfe der Capture-Klausel sehen, [].

Die Capture-Klausel [], auch als Lambda-Introducer bekannt, ermöglicht das Senden von Variablen aus dem umgebenden (Funktions-)Bereich in den Funktionskörper des Lambda-Ausdrucks. Der Funktionskörper des Lambda-Ausdrucks soll die Variable erfassen, wenn er das Objekt empfängt. Ohne die Capture-Klausel [] kann eine Variable nicht aus dem umgebenden Gültigkeitsbereich in den Funktionskörper des Lambda-Ausdrucks gesendet werden. Das folgende Programm veranschaulicht dies mit dem main()-Funktionsbereich als umgebenden Bereich:

#einschließen
Verwenden von Namespace-Std;
int main()

int-ID = 5;
auto-fn = [id]()

cout << id << '\n';
;
fn();
0 zurückgeben;

Die Ausgabe ist 5. Ohne den Namen id innerhalb von [] hätte der Lambda-Ausdruck die Variable id des main()-Funktionsbereichs nicht gesehen.

Aufnahme nach Referenz

Die obige Beispielverwendung der Capture-Klausel ist die Erfassung nach Wert (siehe Details unten). Bei der Erfassung durch Referenz wird der Ort (Speicher) der Variablen, z.G., Die obige ID des umgebenden Geltungsbereichs wird innerhalb des Lambda-Funktionskörpers verfügbar gemacht. Wenn Sie also den Wert der Variablen innerhalb des Lambda-Funktionskörpers ändern, ändert sich der Wert derselben Variablen im umgebenden Bereich. Jeder Variablen, die in der Capture-Klausel wiederholt wird, wird das kaufmännische Und (&) vorangestellt, um dies zu erreichen. Das folgende Programm veranschaulicht dies:

#einschließen
Verwenden von Namespace-Std;
int main()

int-ID = 5; Schwimmer ft = 2.3; char ch = 'A';
auto fn = [&id, &ft, &ch]()

id = 6; ft = 3.4; ch = 'B';
;
fn();
cout << id << ", " <<  ft << ", " <<  ch << '\n';
0 zurückgeben;

Die Ausgabe ist:

    6, 3.4, B

Bestätigen, dass die Variablennamen im Funktionsrumpf des Lambda-Ausdrucks für dieselben Variablen außerhalb des Lambda-Ausdrucks gelten.

Erfassung nach Wert

Beim Erfassen nach Wert wird eine Kopie der Position der Variablen und des umgebenden Gültigkeitsbereichs innerhalb des Lambda-Funktionskörpers verfügbar gemacht. Obwohl die Variable im Rumpf der Lambda-Funktion eine Kopie ist, kann ihr Wert derzeit nicht im Rumpf geändert werden changed. Um eine Erfassung nach Wert zu erreichen, wird jeder Variablen, die in der Capture-Klausel wiederholt wird, nichts vorangestellt. Das folgende Programm veranschaulicht dies:

#einschließen
Verwenden von Namespace-Std;
int main()

int-ID = 5; Schwimmer ft = 2.3; char ch = 'A';
auto fn = [id, ft, ch]()

//id = 6; ft = 3.4; ch = 'B';
cout << id << ", " <<  ft << ", " <<  ch << '\n';
;
fn();
id = 6; ft = 3.4; ch = 'B';
cout << id << ", " <<  ft << ", " <<  ch << '\n';
0 zurückgeben;

Die Ausgabe ist:

5, 2.3, A
6, 3.4, B

Wenn der Kommentarindikator entfernt wird, wird das Programm nicht kompiliert. Der Compiler gibt eine Fehlermeldung aus, dass die Variablen in der Definition des Lambda-Ausdrucks des Funktionskörpers nicht geändert werden können. Obwohl die Variablen innerhalb der Lambda-Funktion nicht geändert werden können, können sie außerhalb der Lambda-Funktion geändert werden, wie die Ausgabe des obigen Programms zeigt.

Mischen von Aufnahmen

Erfassung nach Referenz und Erfassung nach Wert können gemischt werden, wie das folgende Programm zeigt:

#einschließen
Verwenden von Namespace-Std;
int main()

int-ID = 5; Schwimmer ft = 2.3; char ch = 'A'; bool bl = wahr;
auto fn = [id, ft, &ch, &bl]()

ch = 'B'; bl = falsch;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
0 zurückgeben;

Die Ausgabe ist:

    5, 2.3, B, 0

Wenn alle erfasst sind, sind dies als Referenz:

Wenn alle zu erfassenden Variablen per Referenz erfasst werden, reicht nur ein & in der Erfassungsklausel. Das folgende Programm veranschaulicht dies:

#einschließen
Verwenden von Namespace-Std;
int main()

int-ID = 5; Schwimmer ft = 2.3; char ch = 'A'; bool bl = wahr;
Auto-Fn = [&]()

id = 6; ft = 3.4; ch = 'B'; bl = falsch;
;
fn();
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
0 zurückgeben;

Die Ausgabe ist:

6, 3.4, B, 0

Wenn einige Variablen durch Referenzen und andere durch Werte erfasst werden sollen, stellt ein & alle Referenzen dar und den Rest wird jeweils nichts vorangestellt, wie das folgende Programm zeigt:

Verwenden von Namespace-Std;
int main()

int-ID = 5; Schwimmer ft = 2.3; char ch = 'A'; bool bl = wahr;
auto fn = [&, id, ft]()

ch = 'B'; bl = falsch;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
0 zurückgeben;

Die Ausgabe ist:

5, 2.3, B, 0

Beachten Sie, dass & allein (i.e., & ohne Bezeichner) muss das erste Zeichen in der Capture-Klausel sein.

Wenn alle erfasst sind, sind sie nach Wert:

Wenn alle zu erfassenden Variablen nach Wert erfasst werden sollen, genügt ein = in der Erfassungsklausel. Das folgende Programm veranschaulicht dies:

#einschließen
Verwenden von Namespace-Std;
int main()

int-ID = 5; Schwimmer ft = 2.3; char ch = 'A'; bool bl = wahr;
Autofn = [=]()

cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
0 zurückgeben;

Die Ausgabe ist:

5, 2.3, A, 1

Hinweis: = ist ab sofort schreibgeschützt.

Wenn einige Variablen nach Werten und andere nach Referenzen erfasst werden sollen, stellt ein = alle schreibgeschützten kopierten Variablen dar, und der Rest hat jeweils &, wie das folgende Programm zeigt:

#einschließen
Verwenden von Namespace-Std;
int main()

int-ID = 5; Schwimmer ft = 2.3; char ch = 'A'; bool bl = wahr;
auto fn = [=, &ch, &bl]()

ch = 'B'; bl = falsch;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
0 zurückgeben;

Die Ausgabe ist:

5, 2.3, B, 0

Beachten Sie, dass = allein das erste Zeichen in der Capture-Klausel sein muss.

Klassisches Callback-Funktionsschema mit Lambda-Ausdruck

Das folgende Programm zeigt, wie ein klassisches Callback-Funktionsschema mit dem Lambda-Ausdruck erstellt werden kann:

#einschließen
Verwenden von Namespace-Std;
char *Ausgabe;
auto cba = [](char out[])

Ausgang = aus;
;
void PrincipalFunc(char input[], void (*pt)(char[]))

(*pt)(Eingabe);
cout<<"for principal function"<<'\n';

Leere fn()

cout<<"Now"<<'\n';

int main()

char input[] = "für Rückruffunktion";
PrincipalFunc(Eingabe, CBA);
fn();
cout<0 zurückgeben;

Die Ausgabe ist:

für Hauptfunktion
Jetzt
für Rückruffunktion

Denken Sie daran, dass, wenn eine Lambda-Ausdrucksdefinition einer Variablen im globalen Gültigkeitsbereich zugewiesen wird, deren Funktionskörper globale Variablen sehen kann, ohne die Capture-Klausel zu verwenden.

Der Trailing-Return-Typ

Der Rückgabetyp eines Lambda-Ausdrucks ist auto, d. h. der Compiler bestimmt den Rückgabetyp aus dem Rückgabeausdruck (falls vorhanden). Wenn der Programmierer wirklich den Rückgabetyp angeben möchte, wird er dies wie im folgenden Programm tun:

#einschließen
Verwenden von Namespace-Std;
auto fn = [](int param) -> int

int-Antwort = Param + 3;
Antwort zurück;
;
int main()

Autovariable = fn(2);
cout << variab << '\n';
0 zurückgeben;

Die Ausgabe ist 5. Nach der Parameterliste wird der Pfeiloperator eingegeben. Darauf folgt der Rückgabetyp (in diesem Fall int).

Schließung

Betrachten Sie das folgende Codesegment:

struct Cla

int-ID = 5;
char ch = 'a';
obj1, obj2;

Hier ist Cla der Name der Strukturklasse.  Obj1 und obj2 sind zwei Objekte, die aus der Strukturklasse instanziiert werden. Der Lambda-Ausdruck ist in der Implementierung ähnlich. Die Definition der Lambda-Funktion ist eine Art Klasse. Wenn die Lambda-Funktion aufgerufen (aufgerufen) wird, wird ein Objekt aus seiner Definition instanziiert. Dieses Objekt wird Verschluss genannt. Es ist die Schließung, die die Arbeit erledigt, die das Lambda leisten soll.

Beim Codieren des Lambda-Ausdrucks wie in der obigen Struktur werden jedoch obj1 und obj2 durch die entsprechenden Parameterargumente ersetzt. Das folgende Programm veranschaulicht dies:

#einschließen
Verwenden von Namespace-Std;
auto fn = [](int param1, int param2)

int antwort = param1 + param2;
Antwort zurück;
(2, 3);
int main()

autovar = fn;
cout << var << '\n';
0 zurückgeben;

Die Ausgabe ist 5. Die Argumente sind 2 und 3 in Klammern. Beachten Sie, dass der Funktionsaufruf des Lambda-Ausdrucks, fn, kein Argument benötigt, da die Argumente bereits am Ende der Lambda-Funktionsdefinition codiert wurden.

Fazit

Der Lambda-Ausdruck ist eine anonyme Funktion. Es besteht aus zwei Teilen: Klasse und Objekt. Seine Definition ist eine Art Klasse. Beim Aufruf des Ausdrucks wird aus der Definition ein Objekt gebildet. Dieses Objekt wird Verschluss genannt. Es ist die Schließung, die die Arbeit erledigt, die das Lambda leisten soll.

Damit der Lambda-Ausdruck eine Variable von einem äußeren Funktionsbereich erhält, benötigt er eine nicht leere Capture-Klausel in seinem Funktionsrumpf.

So verwenden Sie die GameConqueror-Cheat-Engine unter Linux
Der Artikel enthält eine Anleitung zur Verwendung der GameConqueror-Cheat-Engine unter Linux. Viele Benutzer, die Spiele unter Windows spielen, verwen...
Beste Spielkonsolen-Emulatoren für Linux
Dieser Artikel listet beliebte Spielekonsolen-Emulationssoftware auf, die für Linux verfügbar ist. Emulation ist eine Softwarekompatibilitätsschicht, ...
Beste Linux-Distributionen für Gaming im Jahr 2021
Das Linux-Betriebssystem hat sich weit von seinem ursprünglichen, einfachen, serverbasierten Aussehen entfernt. Dieses Betriebssystem hat sich in den ...