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ßenVerwenden 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ßenVerwenden 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:
5Auß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ßenVerwenden 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 5Hier 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ßenVerwenden 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ßenVerwenden 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, BBestä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ßenVerwenden 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, A6, 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ßenVerwenden 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, 0Wenn 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ßenVerwenden 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, 0Wenn 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, 0Beachten 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ßenVerwenden 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, 1Hinweis: = 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ßenVerwenden 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, 0Beachten 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ßenVerwenden 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<