C ++

Lambda-uttryck i C ++

Lambda-uttryck i C ++

Varför Lambda Expression?

Tänk på följande uttalande:

    int mynt = 52;

Här är myInt en identifierare, en lvalue. 52 är en bokstavlig, en prvalue. Idag är det möjligt att koda en funktion speciellt och placera den i läget 52. En sådan funktion kallas ett lambdauttryck. Tänk också på följande korta program:

#omfatta
använder namnrymd std;
int fn (int par)

int svar = par + 3;
returnera svaret;

int main ()

fn (5);
returnera 0;

Idag är det möjligt att koda en funktion speciellt och placera den i läget för argumentet 5, för funktionsanropet, fn (5). En sådan funktion kallas ett lambdauttryck. Lambdauttrycket (funktionen) i den positionen är ett värde.

Alla bokstäver utom strängen bokstavlig är en prvalue. Lambdauttrycket är en speciell funktionsdesign som passar som en bokstavlig kod. Det är en anonym (ej namngiven) funktion. Den här artikeln förklarar det nya C ++ primära uttrycket, kallat lambdauttrycket. Grundläggande kunskaper i C ++ är ett krav för att förstå den här artikeln.

Artikelinnehåll

  • Illustration av Lambdauttryck
  • Delar av Lambda Expression
  • Fångar
  • Klassiskt återuppringningsfunktionsschema med Lambda-uttryck
  • Den bakre-retur-typen
  • Stängning
  • Slutsats

Illustration av Lambdauttryck

I följande program tilldelas en funktion, som är ett lambdauttryck, en variabel:

#omfatta
använder namnrymd std;
auto fn = [] (int param)

int svar = param + 3;
returnera svaret;
;
int main ()

auto variab = fn (2);
cout << variab << '\n';
returnera 0;

Utgången är:

    5

Utanför huvudfunktionen () finns variabeln, fn. Dess typ är auto. Auto i denna situation innebär att den faktiska typen, såsom int eller float, bestäms av rätt operand för uppdragsoperatören (=). Till höger om uppdragsoperatören finns ett lambdauttryck. Ett lambdauttryck är en funktion utan föregående returtyp. Notera användningen och placeringen av hakparenteserna, []. Funktionen returnerar 5, ett int, som bestämmer typen för fn.

I huvudfunktionen () finns uttalandet:

    auto variab = fn (2);

Detta betyder att fn utanför main () hamnar som identifierare för en funktion. Dess implicita parametrar är de för lambdauttrycket. Typen för variab är auto.

Observera att lambdauttrycket slutar med ett semikolon, precis som klass- eller strukturdefinitionen, slutar med ett semikolon.

I följande program är en funktion, som är ett lambdauttryck som returnerar värdet 5, ett argument till en annan funktion:

#omfatta
använder namnrymd std;
ogiltig otherfn (int no1, int (* ptr) (int))

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

int main ()

otherfn (4, [] (int param)

int svar = param + 3;
returnera svaret;
);
returnera 0;

Utgången är:

    4 5

Det finns två funktioner här, lambdauttrycket och funktionen otherfn (). Lambdauttrycket är det andra argumentet för otherfn (), som kallas main (). Observera att lambdafunktionen (uttryck) inte slutar med semikolon i det här samtalet eftersom det här är ett argument (inte en fristående funktion).

Lambdafunktionsparametern i definitionen av den andrafn () -funktionen är en pekare till en funktion. Pekaren har namnet ptr. Namnet, ptr, används i definitionen av otherfn () för att kalla lambda-funktionen.

Påståendet,

    int no2 = (* ptr) (2);

I definitionen otherfn () kallar den lambdafunktionen med argumentet 2. Returvärdet för samtalet, "(* ptr) (2)" från lambdafunktionen, tilldelas nr 2.

Ovanstående program visar också hur lambdafunktionen kan användas i C ++ återuppringningsfunktionsschemat.

Delar av Lambda Expression

Delarna av en typisk lambdafunktion är som följer:

    [] ()
  • [] är fångstklausulen. Det kan ha föremål.
  • () är för parameterlistan.
  • är för funktionskroppen. Om funktionen står ensam ska den avslutas med ett semikolon.

Fångar

Lambdafunktionsdefinitionen kan tilldelas en variabel eller användas som argument för ett annat funktionsanrop. Definitionen för ett sådant funktionsanrop bör ha som parameter en pekare till en funktion som motsvarar lambdafunktionsdefinitionen.

Lambdafunktionsdefinitionen skiljer sig från den normala funktionsdefinitionen. Den kan tilldelas en variabel i det globala omfånget; den här funktionstilldelade variabeln kan också kodas inuti en annan funktion. När den tilldelas en global omfattningsvariabel kan dess kropp se andra variabler i det globala omfånget. När den tilldelas en variabel i en normal funktionsdefinition kan dess kropp se andra variabler i funktionsomfånget endast med hjälp av fångstklausulen, [].

Fångningsklausulen [], även känd som lambda-introduceraren, tillåter att variabler skickas från det omgivande (funktions) omfånget till lambdauttryckets funktionskropp. Lambdauttryckets funktionskropp sägs fånga variabeln när den tar emot objektet. Utan fångstklausulen [] kan en variabel inte skickas från det omgivande omfånget till lambdauttryckets funktionskropp. Följande program illustrerar detta, med huvudområdet () funktionsomfång, som det omgivande omfånget:

#omfatta
använder namnrymd std;
int main ()

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

cout << id << '\n';
;
fn ();
returnera 0;

Utgången är 5. Utan namnet, id, inuti [], skulle lambdauttrycket inte ha sett variabeln id för huvudfunktionsomfånget ().

Fånga med referens

Ovanstående exempel på användning av fångstklausulen är att fånga efter värde (se detaljer nedan). Vid hämtning av referens, variabelns placering (lagring), e.g., id ovan, av det omgivande omfånget, görs tillgängligt inuti lambdafunktionskroppen. Så om du ändrar värdet på variabeln inuti lambdafunktionens kropp kommer värdet på samma variabel att ändras i det omgivande omfånget. Varje variabel som upprepas i fångstklausulen föregås av bokstaven (&) för att uppnå detta. Följande program illustrerar detta:

#omfatta
använder namnrymd std;
int main ()

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

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

Utgången är:

    6, 3.4, B

Bekräftar att variabelnamnen inuti lambdauttryckets funktionskropp är för samma variabler utanför lambdauttrycket.

Fånga efter värde

För att fånga efter värde görs en kopia av variabelns läge, med det omgivande omfånget, tillgängligt inuti lambdafunktionens kropp. Även om variabeln inuti lambdafunktionens kropp är en kopia, kan dess värde inte ändras inuti kroppen från och med nu. För att uppnå fångst efter värde föregås inte varje variabel som upprepas i fångstklausulen med någonting. Följande program illustrerar detta:

#omfatta
använder namnrymd std;
int main ()

int id = 5; float 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';
returnera 0;

Utgången är:

5, 2.3, A
6, 3.4, B

Om kommentarindikatorn tas bort kommer programmet inte att kompileras. Kompilatorn ger ett felmeddelande om att variablerna i funktionskroppens definition av lambdauttrycket inte kan ändras. Även om variablerna inte kan ändras inuti lambdafunktionen kan de ändras utanför lambdafunktionen, som ovanstående programs utdata visar.

Blandning av fångster

Fånga med referens och fånga efter värde kan blandas, som följande program visar:

#omfatta
använder namnrymd std;
int main ()

int id = 5; float ft = 2.3; char ch = 'A'; bool bl = sant;
auto fn = [id, ft, & ch, & bl] ()

ch = 'B'; bl = falskt;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
returnera 0;

Utgången är:

    5, 2.3, B, 0

När alla fångas, är de som referens:

Om alla variabler som ska fångas fångas genom referens, räcker det bara med en & i fångstklausulen. Följande program illustrerar detta:

#omfatta
använder namnrymd std;
int main ()

int id = 5; float ft = 2.3; char ch = 'A'; bool bl = sant;
auto fn = [&] ()

id = 6; ft = 3.4; ch = 'B'; bl = falskt;
;
fn ();
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
returnera 0;

Utgången är:

6, 3.4, B, 0

Om vissa variabler ska fångas genom referens och andra efter värde, representerar en & alla referenser, och resten kommer vardera inte att föregås av något, som följande program visar:

använder namnrymd std;
int main ()

int id = 5; float ft = 2.3; char ch = 'A'; bool bl = sant;
auto fn = [&, id, ft] ()

ch = 'B'; bl = falskt;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
returnera 0;

Utgången är:

5, 2.3, B, 0

Observera att & ensam (i.e., & inte följt av en identifierare) måste vara det första tecknet i fångstklausulen.

När alla fångas är efter värde:

Om alla variabler som ska fångas ska fångas av värde, räcker bara en = i fångstklausulen. Följande program illustrerar detta:

#omfatta
använder namnrymd std;
int main ()

int id = 5; float ft = 2.3; char ch = 'A'; bool bl = sant;
auto fn = [=] ()

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

Utgången är:

5, 2.3, A, 1

Notera: = är skrivskyddad från och med nu.

Om vissa variabler ska fångas av värde och andra som referens, representerar en = alla skrivskyddade kopierade variabler, och resten har vardera &, som följande program visar:

#omfatta
använder namnrymd std;
int main ()

int id = 5; float ft = 2.3; char ch = 'A'; bool bl = sant;
auto fn = [=, & ch, & bl] ()

ch = 'B'; bl = falskt;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
returnera 0;

Utgången är:

5, 2.3, B, 0

Observera att = ensam måste vara det första tecknet i fångstklausulen.

Klassiskt återuppringningsfunktionsschema med Lambda-uttryck

Följande program visar hur ett klassiskt återuppringningsfunktionsschema kan göras med lambdauttrycket:

#omfatta
använder namnrymd std;
char * output;
auto cba = [] (char out [])

utgång = ut;
;
void principalFunc (char input [], void (* pt) (char []))

(* pt) (ingång);
cout<<"for principal function"<<'\n';

ogiltigt fn ()

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

int main ()

char input [] = "för återuppringningsfunktion";
principalFunc (input, cba);
fn ();
cout<returnera 0;

Utgången är:

för huvudfunktion
Nu
för återuppringningsfunktion

Minns att när en lambdauttrycksdefinition tilldelas en variabel i det globala omfånget, kan dess funktionsdel se globala variabler utan att använda fångstklausulen.

Den bakre-retur-typen

Returtypen för ett lambdauttryck är auto, vilket betyder att kompilatorn bestämmer returtypen från returuttrycket (om det finns). Om programmeraren verkligen vill ange returtypen gör han det som i följande program:

#omfatta
använder namnrymd std;
auto fn = [] (int param) -> int

int svar = param + 3;
returnera svaret;
;
int main ()

auto variab = fn (2);
cout << variab << '\n';
returnera 0;

Utgången är 5. Efter parameterlistan skrivs piloperatören. Detta följs av returtypen (int i detta fall).

Stängning

Tänk på följande kodsegment:

struct Cla

int id = 5;
char ch = 'a';
obj1, obj2;

Här är Cla namnet på struct-klassen.  Obj1 och obj2 är två objekt som kommer att instantieras från struct-klassen. Lambdauttryck är liknande i implementeringen. Lambdafunktionens definition är en slags klass. När lambdafunktionen kallas (åberopas) instansieras ett objekt från dess definition. Detta objekt kallas förslutning. Det är stängningen som gör det arbete som lambda förväntas göra.

Kodning av lambdauttrycket som strukturen ovan kommer emellertid att obj1 och obj2 ersätts av motsvarande parameters argument. Följande program illustrerar detta:

#omfatta
använder namnrymd std;
auto fn = [] (int param1, int param2)

int svar = param1 + param2;
returnera svaret;
(2, 3);
int main ()

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

Utgången är 5. Argumenten är 2 och 3 inom parentes. Observera att lambdauttrycksfunktionsanropet, fn, inte tar något argument eftersom argumenten redan har kodats i slutet av lambdafunktionsdefinitionen.

Slutsats

Lambdauttrycket är en anonym funktion. Det är i två delar: klass och objekt. Dess definition är en slags klass. När uttrycket kallas bildas ett objekt från definitionen. Detta objekt kallas förslutning. Det är stängningen som gör det arbete som lambda förväntas göra.

För att lambdauttrycket ska få en variabel från ett yttre funktionsomfång behöver det en icke-tom fångstklausul i sin funktionskropp.

Mus WinMouse låter dig anpassa och förbättra muspekarens rörelse på Windows PC
WinMouse låter dig anpassa och förbättra muspekarens rörelse på Windows PC
Om du vill förbättra standardfunktionerna för din muspekare, använd freeware WinMouse. Det lägger till fler funktioner som hjälper dig att få ut det m...
Mus Mus vänsterklicka på knappen fungerar inte på Windows 10
Mus vänsterklicka på knappen fungerar inte på Windows 10
Om du använder en dedikerad mus med din bärbara dator eller stationära dator men musens vänsterklick-knapp fungerar inte på Windows 10/8/7 av någon an...
Mus Markören hoppar eller rör sig slumpmässigt när du skriver in Windows 10
Markören hoppar eller rör sig slumpmässigt när du skriver in Windows 10
Om du upptäcker att muspekaren hoppar eller rör sig på egen hand, automatiskt, slumpmässigt när du skriver in Windows-bärbar dator eller dator, kan nå...