C++

Lambda-expressies in C++

Lambda-expressies in C++

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:

#include
namespace 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:

#include
namespace 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:

    5

Buiten 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:

#include
namespace 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 5

Er 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:

#include
namespace 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:

#include
namespace 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, B

Bevestigen 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:

#include
namespace 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, A
6, 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:

#include
namespace 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, 0

Wanneer 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:

#include
namespace 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, 0

Als 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, 0

Merk 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:

#include
namespace 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, 1

Opmerking: = 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:

#include
namespace 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, 0

Merk 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:

#include
namespace 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<retourneer 0;

De uitvoer is:

voor hoofdfunctie:
Nu
voor terugbelfunctie:

Bedenk dat wanneer een lambda-expressiedefinitie wordt toegewezen aan een variabele in het globale bereik, de functietekst globale variabelen kan zien zonder de capture-clausule te gebruiken.

Het trailing-return-type

Het retourtype van een lambda-expressie is auto, wat betekent dat de compiler het retourtype bepaalt uit de retourexpressie (indien aanwezig). Als de programmeur echt het retourtype wil aangeven, dan zal hij dat doen zoals in het volgende programma:

#include
namespace std; gebruiken;
auto fn = [](int param) -> int

int antwoord = param + 3;
antwoord terug;
;
int hoofd()

automatische variabele = fn(2);
cout << variab << '\n';
retourneer 0;

De uitvoer is 5. Na de parameterlijst wordt de pijloperator getypt. Dit wordt gevolgd door het retourtype (int in dit geval).

Sluiting

Overweeg het volgende codesegment:

struct Cla

int-id = 5;
char ch = 'een';
obj1, obj2;

Hier is Cla de naam van de struct-klasse.  Obj1 en obj2 zijn twee objecten die worden geïnstantieerd uit de struct-klasse. Lambda-expressie is vergelijkbaar in implementatie. De lambda-functiedefinitie is een soort klasse. Wanneer de lambda-functie wordt aangeroepen (aangeroepen), wordt een object geïnstantieerd vanuit zijn definitie. Dit object wordt een sluiting genoemd. Het is de sluiting die het werk doet dat de lambda verwacht te doen.

Bij het coderen van de lambda-expressie zoals de bovenstaande struct worden obj1 en obj2 echter vervangen door de argumenten van de overeenkomstige parameters. Het volgende programma illustreert dit:

#include
namespace std; gebruiken;
auto fn = [](int param1, int param2)

int antwoord = param1 + param2;
antwoord terug;
(2, 3);
int hoofd()

auto var = fn;
cout << var << '\n';
retourneer 0;

De uitvoer is 5. De argumenten zijn 2 en 3 tussen haakjes. Merk op dat de functieaanroep van de lambda-expressie, fn, geen enkel argument aanneemt, aangezien de argumenten al zijn gecodeerd aan het einde van de definitie van de lambda-functie.

Conclusie

De lambda-expressie is een anonieme functie. Het bestaat uit twee delen: klasse en object. De definitie ervan is een soort klasse. Wanneer de uitdrukking wordt aangeroepen, wordt een object gevormd uit de definitie. Dit object wordt een sluiting genoemd. Het is de sluiting die het werk doet dat de lambda verwacht te doen.

Om ervoor te zorgen dat de lambda-expressie een variabele van een buitenste functiebereik ontvangt, heeft deze een niet-lege capture-clausule nodig in de functietekst.

Installeer de nieuwste Dolphin Emulator voor Gamecube & Wii op Linux
Met de Dolphin Emulator kun je de door jou gekozen Gamecube- en Wii-spellen spelen op Linux Personal Computers (pc). Omdat het een vrij beschikbare e...
Hoe de GameConqueror Cheat Engine in Linux te gebruiken
Het artikel bevat een handleiding over het gebruik van de GameConqueror cheat-engine in Linux. Veel gebruikers die games op Windows spelen, gebruiken ...
Beste gameconsole-emulators voor Linux
Dit artikel bevat een lijst van populaire emulatiesoftware voor gameconsoles die beschikbaar is voor Linux. Emulatie is een softwarecompatibiliteitsla...