Waarom Lambda-expressie??
Denk aan de volgende stelling:
int mijnInt = 52;Hier is myInt een identifier, een lvalue. 52 is een letterlijke, een pr-waarde. Tegenwoordig is het mogelijk om een functie speciaal te coderen en in de positie 52 . te plaatsen. Zo'n functie wordt een lambda-expressie genoemd. Denk ook aan het volgende korte programma:
#includenamespace std; gebruiken;
int fn(int par)
int antwoord = par + 3;
antwoord terug;
int hoofd()
fn(5);
retourneer 0;
Tegenwoordig is het mogelijk om een functie speciaal te coderen en deze in de positie van het argument van 5 van de functieaanroep fn(5) te plaatsen. Zo'n functie wordt een lambda-expressie genoemd. De lambda-uitdrukking (functie) in die positie is een prwaarde.
Elke letterlijke behalve de letterlijke tekenreeks is een prwaarde. De lambda-expressie is een speciaal functieontwerp dat als een letterlijke waarde in code zou passen. Het is een anonieme (niet nader genoemde) functie. In dit artikel wordt de nieuwe primaire C++-expressie uitgelegd, de lambda-expressie genoemd. Basiskennis in C++ is een vereiste om dit artikel te begrijpen.
Artikel Inhoud
- Illustratie van Lambda-expressie
- Delen van Lambda Expression
- opnamen
- Klassiek callback-functieschema met Lambda-expressie
- Het trailing-return-type
- Sluiting
- Conclusie
Illustratie van Lambda-expressie
In het volgende programma wordt een functie, die een lambda-expressie is, toegewezen aan een variabele:
#includenamespace std; gebruiken;
auto fn = [](int param)
int antwoord = param + 3;
antwoord terug;
;
int hoofd()
automatische variabele = fn(2);
cout << variab << '\n';
retourneer 0;
De uitvoer is:
5Buiten de functie main() is er de variabele fn. Het type is auto. Auto betekent in deze situatie dat het werkelijke type, zoals int of float, wordt bepaald door de juiste operand van de toewijzingsoperator (=). Rechts van de toewijzingsoperator staat een lambda-expressie. Een lambda-expressie is een functie zonder het voorgaande retourtype. Let op het gebruik en de positie van de vierkante haken, []. De functie retourneert 5, een int, die het type voor fn . zal bepalen.
In de functie main() staat de instructie:
automatische variabele = fn(2);Dit betekent dat fn outside main(), eindigt als de identifier voor een functie. De impliciete parameters zijn die van de lambda-expressie. Het type voor variab is auto.
Merk op dat de lambda-expressie eindigt met een puntkomma, net als de class- of struct-definitie, eindigt met een puntkomma.
In het volgende programma is een functie, die een lambda-expressie is die de waarde 5 retourneert, een argument voor een andere functie:
#includenamespace std; gebruiken;
void otherfn (int no1, int (*ptr)(int))
int no2 = (*ptr)(2);
cout << no1 << " << no2 << '\n';
int hoofd()
anderefn(4, [](int param)
int antwoord = param + 3;
antwoord terug;
);
retourneer 0;
De uitvoer is:
4 5Er zijn hier twee functies, de lambda-expressie en de otherfn()-functie. De lambda-expressie is het tweede argument van de otherfn(), aangeroepen in main(). Merk op dat de lambda-functie (expressie) niet eindigt met een puntkomma in deze aanroep, omdat het hier een argument is (geen zelfstandige functie).
De parameter lambda-functie in de definitie van de functie otherfn() is een verwijzing naar een functie. De aanwijzer heeft de naam, ptr. De naam, ptr, wordt gebruikt in de otherfn()-definitie om de lambda-functie aan te roepen.
de verklaring,
int no2 = (*ptr)(2);In de otherfn()-definitie roept het de lambda-functie aan met een argument van 2. De retourwaarde van de aanroep, "(*ptr)(2)" van de lambda-functie, wordt toegewezen aan no2.
Het bovenstaande programma laat ook zien hoe de lambda-functie kan worden gebruikt in het C++ callback-functieschema.
Delen van Lambda Expression
De onderdelen van een typische lambdafunctie zijn als volgt:
[] ()- [] is de capture-clausule. Het kan items hebben.
- () is voor de parameterlijst.
- is voor de functie body. Als de functie op zichzelf staat, moet deze eindigen met een puntkomma.
opnamen
De lambda-functiedefinitie kan worden toegewezen aan een variabele of worden gebruikt als argument voor een andere functieaanroep. De definitie voor zo'n functie-aanroep zou als parameter een pointer naar een functie moeten hebben, overeenkomend met de lambda-functiedefinitie.
De lambda-functiedefinitie verschilt van de normale functiedefinitie. Het kan worden toegewezen aan een variabele in het globale bereik; deze functie-toegewezen-aan-variabele kan ook binnen een andere functie worden gecodeerd. Wanneer toegewezen aan een globale bereikvariabele, kan de hoofdtekst andere variabelen in het globale bereik zien. Wanneer toegewezen aan een variabele binnen een normale functiedefinitie, kan de hoofdtekst ervan andere variabelen in het functiebereik alleen zien met de hulp van de capture-clausule, [].
Met de capture-clausule [], ook bekend als de lambda-introducer, kunnen variabelen worden verzonden vanuit de omringende (functie) scope naar de functietekst van de lambda-expressie. Er wordt gezegd dat de functietekst van de lambda-expressie de variabele vastlegt wanneer deze het object ontvangt. Zonder de capture-clausule [] kan een variabele niet vanuit het omringende bereik naar de functietekst van de lambda-expressie worden verzonden. Het volgende programma illustreert dit, met het functiebereik main() als het omringende bereik:
#includenamespace std; gebruiken;
int hoofd()
int-id = 5;
automatische fn = [id]()
cout << id << '\n';
;
fn();
retourneer 0;
De uitvoer is: 5. Zonder de naam, id, binnen [], zou de lambda-expressie de variabele id van het functiebereik main() niet hebben gezien.
Vastleggen op referentie
Het bovenstaande voorbeeld van het gebruik van de capture-clausule is het vastleggen op waarde (zie details hieronder). Bij het vastleggen door middel van referentie, de locatie (opslag) van de variabele, e.g., id hierboven, van de omringende scope, wordt beschikbaar gemaakt in de lambda-functiebody function. Dus, het veranderen van de waarde van de variabele in de lambda-functie zal de waarde van diezelfde variabele in het omringende bereik veranderen. Elke variabele die in de capture-clausule wordt herhaald, wordt voorafgegaan door het ampersand (&) om dit te bereiken. Het volgende programma illustreert dit:
#includenamespace std; gebruiken;
int hoofd()
int-id = 5; vlotter voet = 2.3; char ch = 'A';
auto fn = [&id, &ft, &ch]()
id = 6; voet = 3.4; ch = 'B';
;
fn();
cout << id << ", " << ft << ", " << ch << '\n';
retourneer 0;
De uitvoer is:
6, 3.4, BBevestigen dat de variabelenamen binnen de functietekst van de lambda-expressie voor dezelfde variabelen buiten de lambda-expressie zijn.
Vastleggen op waarde
Bij het vastleggen op waarde wordt een kopie van de locatie van de variabele, van het omringende bereik, beschikbaar gemaakt in de lambda-functie. Hoewel de variabele in de body van de lambda-functie een kopie is, kan de waarde vanaf nu niet meer in de body worden gewijzigd. Om capture op waarde te bereiken, wordt elke variabele die in de capture-clausule wordt herhaald, nergens door voorafgegaan. Het volgende programma illustreert dit:
#includenamespace std; gebruiken;
int hoofd()
int-id = 5; vlotter voet = 2.3; char ch = 'A';
auto fn = [id, ft, ch]()
//id = 6; voet = 3.4; ch = 'B';
cout << id << ", " << ft << ", " << ch << '\n';
;
fn();
id = 6; voet = 3.4; ch = 'B';
cout << id << ", " << ft << ", " << ch << '\n';
retourneer 0;
De uitvoer is:
5, 2.3, A6, 3.4, B
Als de commentaarindicator wordt verwijderd, zal het programma niet compileren. De compiler zal een foutmelding geven dat de variabelen binnen de functie-definitie van de lambda-expressie niet kunnen worden gewijzigd. Hoewel de variabelen niet kunnen worden gewijzigd binnen de lambda-functie, kunnen ze buiten de lambda-functie worden gewijzigd, zoals de uitvoer van het bovenstaande programma laat zien.
Opnamen mixen
Vastleggen via referentie en vastleggen op waarde kan worden gemengd, zoals het volgende programma laat zien:
#includenamespace std; gebruiken;
int hoofd()
int-id = 5; vlotter voet = 2.3; char ch = 'A'; bool bl = waar;
auto fn = [id, ft, &ch, &bl]()
ch = 'B'; bl = onwaar;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
retourneer 0;
De uitvoer is:
5, 2.3, B, 0Wanneer ze allemaal zijn vastgelegd, zijn door verwijzing:
Als alle variabelen die moeten worden vastgelegd door verwijzing worden vastgelegd, dan is slechts één & voldoende in de capture-clausule. Het volgende programma illustreert dit:
#includenamespace std; gebruiken;
int hoofd()
int-id = 5; vlotter voet = 2.3; char ch = 'A'; bool bl = waar;
automatisch fn = [&]()
id = 6; voet = 3.4; ch = 'B'; bl = onwaar;
;
fn();
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
retourneer 0;
De uitvoer is:
6, 3.4, B, 0Als sommige variabelen moeten worden vastgelegd door verwijzing en andere door waarde, dan zal één & alle verwijzingen vertegenwoordigen, en de rest zal door niets worden voorafgegaan, zoals het volgende programma laat zien:
namespace std; gebruiken;int hoofd()
int-id = 5; vlotter voet = 2.3; char ch = 'A'; bool bl = waar;
auto fn = [&, id, ft]()
ch = 'B'; bl = onwaar;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
retourneer 0;
De uitvoer is:
5, 2.3, B, 0Merk op dat & alleen (i.e., & niet gevolgd door een identifier) moet het eerste teken zijn in de capture-clausule.
Als ze allemaal zijn vastgelegd, zijn ze op waarde:
Als alle variabelen die moeten worden vastgelegd door waarde moeten worden vastgelegd, dan is slechts één = voldoende in de capture-clausule. Het volgende programma illustreert dit:
#includenamespace std; gebruiken;
int hoofd()
int-id = 5; vlotter voet = 2.3; char ch = 'A'; bool bl = waar;
automatisch fn = [=]()
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
retourneer 0;
De uitvoer is:
5, 2.3, A, 1Opmerking: = is vanaf nu alleen-lezen.
Als sommige variabelen moeten worden vastgelegd op waarde en andere op referentie, dan zal één = alle alleen-lezen gekopieerde variabelen vertegenwoordigen, en de rest zal elk & hebben, zoals het volgende programma laat zien:
#includenamespace std; gebruiken;
int hoofd()
int-id = 5; vlotter voet = 2.3; char ch = 'A'; bool bl = waar;
auto fn = [=, &ch, &bl]()
ch = 'B'; bl = onwaar;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
retourneer 0;
De uitvoer is:
5, 2.3, B, 0Merk op dat = alleen het eerste teken in de capture-clausule moet zijn.
Klassiek callback-functieschema met Lambda-expressie
Het volgende programma laat zien hoe een klassiek callback-functieschema kan worden uitgevoerd met de lambda-expressie:
#includenamespace std; gebruiken;
char * uitvoer;
auto cba = [](char uit[])
uitgang = uit;
;
void principalFunc(char input[], void (*pt)(char[]))
(*pt)(invoer);
cout<<"for principal function"<<'\n';
ongeldig fn()
cout<<"Now"<<'\n';
int hoofd()
char input [] = "voor terugbelfunctie";
principalFunc(invoer, cba);
fn();
cout<