Lambda -udtryk i C ++

Lambda Expressions C



Hvorfor Lambda Expression?

Overvej følgende udsagn:

intminInt= 52;

Her er myInt en identifikator, en lværdi. 52 er en bogstavelig, en prvalue. I dag er det muligt at kode en funktion specielt og sætte den i positionen 52. En sådan funktion kaldes et lambda -udtryk. Overvej også følgende korte program:







#omfatte

ved brug af navnerumtimer;

intfn(intigennem)

{

intsvar=igennem+ 3;

Vend tilbagesvar;

}


intvigtigste()

{

fn(5);



Vend tilbage 0;

}

I dag er det muligt at kode en funktion specielt og sætte den i positionen for argumentet 5, for funktionsopkaldet, fn (5). En sådan funktion kaldes et lambda -udtryk. Lambda -udtrykket (funktion) i den position er en prvalue.



Enhver bogstavelig undtagen strengen bogstavelig er en prvalue. Lambda -udtrykket er et specialfunktionsdesign, der ville passe som en bogstavelig kode. Det er en anonym (ikke navngivet) funktion. Denne artikel forklarer det nye C ++ - primære udtryk, kaldet lambda -udtrykket. Grundlæggende viden i C ++ er et krav for at forstå denne artikel.



Artikelindhold

Illustration af Lambda Expression

I det følgende program er en funktion, som er et lambda -udtryk, tildelt en variabel:





#omfatte

ved brug af navnerumtimer;

autofn= [](inthold op)

{

intsvar=hold op+ 3;

Vend tilbagesvar;

};


intvigtigste()

{

autovariab=fn(2);

koste <<variab<< ' n';


Vend tilbage 0;

}

Outputtet er:

5

Uden for hovedfunktionen () er variablen fn. Dens type er auto. Auto i denne situation betyder, at den faktiske type, såsom int eller float, bestemmes af tildelingsoperatørens højre operand (=). Til højre for tildelingsoperatoren er et lambda -udtryk. Et lambda -udtryk er en funktion uden den foregående returtype. Bemærk brugen og placeringen af ​​de firkantede parenteser, []. Funktionen returnerer 5, en int, som bestemmer typen for fn.



I hovedfunktionen () er der udsagnet:

autovariab=fn(2);

Dette betyder, at fn uden for main () ender som identifikator for en funktion. Dets implicitte parametre er dem for lambda -udtrykket. Typen til variab er auto.

Bemærk, at lambda -udtrykket ender med et semikolon, ligesom klasse- eller struct -definitionen ender med et semikolon.

I det følgende program er en funktion, som er et lambda -udtryk, der returnerer værdien 5, et argument til en anden funktion:

#omfatte

ved brug af navnerumtimer;

ugyldigandetfn(intno1,int (*ptr)(int))

{

intnr2= (*ptr)(2);

koste <<nr. 1<< '' <<nr2<< ' n';

}


intvigtigste()

{

andetfn(4,[](inthold op)

{

intsvar=hold op+ 3;

Vend tilbagesvar;

});


Vend tilbage 0;
}

Outputtet er:

Fire. Fem

Der er to funktioner her, lambda -udtrykket og den andenfn () -funktion. Lambda -udtrykket er det andet argument for det andetfn (), kaldet main (). Bemærk, at lambda-funktionen (udtryk) ikke slutter med et semikolon i dette opkald, fordi det her er et argument (ikke en enkeltstående funktion).

Lambda -funktionsparameteren i definitionen af ​​funktionen otherfn () er en markør til en funktion. Markøren har navnet, ptr. Navnet, ptr, bruges i definitionen otherfn () til at kalde lambda -funktionen.

Erklæringen,

intnr2= (*ptr)(2);

I definitionen otherfn () kalder den lambda -funktionen med et argument på 2. Returværdien af ​​opkaldet, '(*ptr) (2)' fra lambda -funktionen, tildeles no2.

Ovenstående program viser også, hvordan lambda -funktionen kan bruges i C ++ callback -funktionsskemaet.

Dele af Lambda Expression

Dele af en typisk lambda -funktion er som følger:

[] () {}
  • [] er indfangningsklausulen. Det kan have varer.
  • () er for parameterlisten.
  • {} er til funktionskroppen. Hvis funktionen står alene, skal den ende med et semikolon.

Fanger

Lambda -funktionsdefinitionen kan tildeles en variabel eller bruges som argument til et andet funktionsopkald. Definitionen for et sådant funktionsopkald skal have en parameter, en markør til en funktion, der svarer til lambda -funktionsdefinitionen.

Lambda -funktionsdefinitionen er forskellig fra den normale funktionsdefinition. Det kan tildeles en variabel i det globale omfang; denne funktion-tildelte-til-variabel kan også kodes inde i en anden funktion. Når den er tildelt en global omfangsvariabel, kan dens krop se andre variabler i det globale omfang. Når den er tildelt en variabel inden for en normal funktionsdefinition, kan dens krop kun se andre variabler i funktionsomfanget med capture -klausulens hjælp, [].

Capture-klausulen [], også kendt som lambda-introduceren, gør det muligt at sende variabler fra det omgivende (funktions) omfang til lambda-udtrykets funktionslegeme. Lambda -udtrykets funktionslegeme siges at fange variablen, når den modtager objektet. Uden capture -klausulen [] kan en variabel ikke sendes fra det omgivende omfang til lambda -udtrykets funktionslegeme. Det følgende program illustrerer dette med hovedfunktionsomfanget () som det omgivende omfang:

#omfatte

ved brug af navnerumtimer;

intvigtigste()

{

intid= 5;


autofn= [id]()

{

koste <<id<< ' n';

};

fn();


Vend tilbage 0;

}

Outputtet er 5 . Uden navnet, id, inde i [], ville lambda -udtrykket ikke have set variablen id for main () funktionsomfanget.

Optagelse med reference

Ovenstående eksempel på brug af indfangningsklausulen er at fange efter værdi (se detaljer nedenfor). Ved registrering ved reference gøres placeringen (lagringen) af variablen, f.eks. Id ovenfor, af det omgivende omfang tilgængelig inde i lambda -funktionslegemet. Så ændring af værdien af ​​variablen inde i lambda -funktionslegemet vil ændre værdien af ​​den samme variabel i det omgivende omfang. Hver variabel, der gentages i indfangningsklausulen, går forud for ampersand (&) for at opnå dette. Følgende program illustrerer dette:

#omfatte

ved brug af navnerumtimer;

intvigtigste()

{

intid= 5; flydeft= 2.3; forkælelsekap= 'TIL';

autofn= [&id,&ft,&kap]()

{

id= 6;ft= 3.4;kap= 'B';

};

fn();

koste <<id<< ',' <<ft<< ',' <<kap<< ' n';

Vend tilbage 0;

}

Outputtet er:

6, 3.4, B

Bekræfter, at variabelnavne inde i lambda -udtrykets funktionslegeme er for de samme variabler uden for lambda -udtrykket.

Optagelse efter værdi

Ved registrering af værdi stilles en kopi af variablens placering, af det omgivende omfang til rådighed inde i lambda -funktionslegemet. Selvom variablen inde i lambda -funktionskroppen er en kopi, kan dens værdi ikke ændres inde i kroppen fra nu af. For at opnå indfangning efter værdi er hver variabel gentaget i indfangningsklausulen ikke forud for noget. Følgende program illustrerer dette:

#omfatte

ved brug af navnerumtimer;

intvigtigste()

{

intid= 5; flydeft= 2.3; forkælelsekap= 'TIL';

autofn= [id, ft, ch]()

{

// id = 6; ft = 3,4; ch = 'B';

koste <<id<< ',' <<ft<< ',' <<kap<< ' n';

};

fn();

id= 6;ft= 3.4;kap= 'B';

koste <<id<< ',' <<ft<< ',' <<kap<< ' n';

Vend tilbage 0;

}

Outputtet er:

5, 2.3, A

6, 3.4, B

Hvis kommentarindikatoren fjernes, vil programmet ikke kompilere. Kompilatoren udsender en fejlmeddelelse om, at variablerne i funktionslegemets definition af lambda -udtrykket ikke kan ændres. Selvom variablerne ikke kan ændres inde i lambda -funktionen, kan de ændres uden for lambda -funktionen, som ovenstående programs output viser.

Mixing Captures

Fangst efter reference og indfangning efter værdi kan blandes, som følgende program viser:

#omfatte

ved brug af navnerumtimer;

intvigtigste()

{

intid= 5; flydeft= 2.3; forkælelsekap= 'TIL'; boolbl= sand;


autofn= [id, ft,&ch,&bl]()

{

kap= 'B';bl= falsk;

koste <<id<< ',' <<ft<< ',' <<kap<< ',' <<bl<< ' n';

};

fn();


Vend tilbage 0;

}

Outputtet er:

5, 2.3, B, 0

Når alle er fanget, er de ved reference:

Hvis alle variabler, der skal fanges, fanges ved reference, er kun en & tilstrækkelig i indfangningsklausulen. Følgende program illustrerer dette:

#omfatte

ved brug af navnerumtimer;

intvigtigste()

{

intid= 5; flydeft= 2.3; forkælelsekap= 'TIL'; boolbl= sand;


autofn= [&]()

{

id= 6;ft= 3.4;kap= 'B';bl= falsk;

};

fn();

koste <<id<< ',' <<ft<< ',' <<kap<< ',' <<bl<< ' n';


Vend tilbage 0;

}

Outputtet er:

6, 3.4, B, 0

Hvis nogle variabler skal indfanges ved reference og andre efter værdi, repræsenterer en & alle referencerne, og resten vil hver ikke gå forud for noget, som følgende program viser:

ved brug af navnerumtimer;

intvigtigste()

{

intid= 5; flydeft= 2.3; forkælelsekap= 'TIL'; boolbl= sand;


autofn= [&, id, ft]()

{

kap= 'B';bl= falsk;

koste <<id<< ',' <<ft<< ',' <<kap<< ',' <<bl<< ' n';

};

fn();


Vend tilbage 0;

}

Outputtet er:

5, 2.3, B, 0

Bemærk, at & alene (dvs. & ikke efterfulgt af en identifikator) skal være det første tegn i indfangningsklausulen.

Når alle er fanget, er efter værdi:

Hvis alle variabler, der skal fanges, skal indfanges efter værdi, er kun én = tilstrækkelig i indfangningsklausulen. Følgende program illustrerer dette:

#omfatte

ved brug af navnerumtimer;

intvigtigste()
{

intid= 5; flydeft= 2.3; forkælelsekap= 'TIL'; boolbl= sand;


autofn= [=]()

{

koste <<id<< ',' <<ft<< ',' <<kap<< ',' <<bl<< ' n';

};

fn();


Vend tilbage 0;


}

Outputtet er:

5, 2.3, A, 1

Bemærk : = er skrivebeskyttet fra nu af.

Hvis nogle variabler skal fanges med værdi og andre ved reference, repræsenterer en = alle de skrivebeskyttede kopierede variabler, og resten vil hver have &, som følgende program viser:

#omfatte

ved brug af navnerumtimer;

intvigtigste()

{

intid= 5; flydeft= 2.3; forkælelsekap= 'TIL'; boolbl= sand;


autofn= [=,&ch,&bl]()

{

kap= 'B';bl= falsk;

koste <<id<< ',' <<ft<< ',' <<kap<< ',' <<bl<< ' n';

};

fn();


Vend tilbage 0;

}

Outputtet er:

5, 2.3, B, 0

Bemærk, at = alene skal være det første tegn i indfangningsklausulen.

Klassisk tilbagekaldelsesprogram med Lambda -udtryk

Følgende program viser, hvordan et klassisk callback -funktionsskema kan udføres med lambda -udtrykket:

#omfatte

ved brug af navnerumtimer;

forkælelse *produktion;


autocba= [](forkælelseud[])

{

produktion=ud;

};



ugyldigmainFunc(forkælelseinput[],ugyldig (*til)(forkælelse[]))

{

(*til)(input);

koste<<'til hovedfunktion'<<' n';

}


ugyldigfn()

{

koste<<'Nu'<<' n';

}


intvigtigste()

{

forkælelseinput[] = 'til tilbagekaldsfunktion';

mainFunc(input, cba);

fn();

koste<<produktion<<' n';



Vend tilbage 0;

}

Outputtet er:

til hovedfunktion

Nu

til tilbagekaldsfunktion

Husk, at når en lambda -udtryksdefinition er tildelt en variabel i det globale omfang, kan dets funktionslegeme se globale variabler uden at anvende capture -klausulen.

Den efterfølgende-retur-type

Returtypen for et lambda -udtryk er automatisk, hvilket betyder, at kompilatoren bestemmer returtypen ud fra returudtrykket (hvis det findes). Hvis programmøren virkelig vil angive returtypen, vil han gøre det som i følgende program:

#omfatte

ved brug af navnerumtimer;

autofn= [](inthold op) -> int

{

intsvar=hold op+ 3;

Vend tilbagesvar;

};


intvigtigste()

{

autovariab=fn(2);

koste <<variab<< ' n';


Vend tilbage 0;

}

Outputtet er 5. Efter parameterlisten indtastes piloperatoren. Dette efterfølges af returtypen (int i dette tilfælde).

Lukning

Overvej følgende kodesegment:

strukturCla

{

intid= 5;

forkælelsekap= 'til';

}obj1, obj2;

Her er Cla navnet på struct -klassen. Obj1 og obj2 er to objekter, der vil blive instantieret fra struct -klassen. Lambda udtryk er ens i implementeringen. Lambda -funktionens definition er en slags klasse. Når lambda -funktionen kaldes (påberåbes), instantieres et objekt fra dets definition. Dette objekt kaldes en lukning. Det er lukningen, der udfører det arbejde, lambda forventes at udføre.

Imidlertid vil kodning af lambda -udtrykket som strukturen ovenfor få obj1 og obj2 erstattet af de tilsvarende parameteres argumenter. Følgende program illustrerer dette:

#omfatte

ved brug af navnerumtimer;

autofn= [](intparam1,intparam2)

{

intsvar=param1+param2;

Vend tilbagesvar;

} (2,3);


intvigtigste()

{

autohvor=fn;

koste <<hvor<< ' n';


Vend tilbage 0;

}

Output er 5. Argumenterne er 2 og 3 i parentes. Bemærk, at lambda -udtryksfunktionsopkaldet, fn, ikke tager noget argument, da argumenterne allerede er blevet kodet i slutningen af ​​lambda -funktionsdefinitionen.

Konklusion

Lambda -udtrykket er en anonym funktion. Det er i to dele: klasse og objekt. Dets definition er en slags klasse. Når udtrykket kaldes, dannes et objekt ud fra definitionen. Dette objekt kaldes en lukning. Det er lukningen, der udfører det arbejde, lambda forventes at udføre.

For at lambda-udtrykket kan modtage en variabel fra et ydre funktionsomfang, har det brug for en ikke-tom fangstklausul i dets funktionslegeme.