Lambda Expressions i C ++

Lambda Expressions C



Varför Lambda Expression?

Tänk på följande uttalande:

intminInt= 52;

Här är myInt en identifierare, en l -värde. 52 är en bokstavlig, en värde. Idag är det möjligt att koda en funktion speciellt och sätta den i positionen 52. En sådan funktion kallas ett lambda -uttryck. Tänk också på följande korta program:







#omfatta

använder sig av namnrymdtimmar;

intfn(intgenom)

{

intsvar=genom+ 3;

lämna tillbakasvar;

}


inthuvud()

{

fn(5);



lämna tillbaka 0;

}

Idag är det möjligt att koda en funktion speciellt och placera den i positionen för argumentet 5, för funktionsanropet, fn (5). En sådan funktion kallas ett lambda -uttryck. Lambda -uttrycket (funktionen) i den positionen är en värde.



Varje bokstav utom strängen bokstavlig är en prvalue. Lambda -uttrycket är en specialfunktionsdesign som skulle passa som bokstavligen i koden. Det är en anonym (namnlös) funktion. Den här artikeln förklarar det nya C ++ - primära uttrycket, kallat lambda -uttrycket. Grundläggande kunskaper i C ++ är ett krav för att förstå denna artikel.



Artikelinnehåll

Illustration av Lambda -uttryck

I följande program tilldelas en funktion, som är ett lambda -uttryck, till en variabel:





#omfatta

använder sig av namnrymdtimmar;

bilfn= [](intsluta)

{

intsvar=sluta+ 3;

lämna tillbakasvar;

};


inthuvud()

{

bilvariab=fn(2);

kosta <<variab<< ' n';


lämna tillbaka 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 tilldelningsoperatörens rätt operand (=). Till höger om tilldelningsoperatören finns ett lambda -uttryck. Ett lambda -uttryck är en funktion utan föregående returtyp. Notera användningen och placeringen av hakparenteserna, []. Funktionen returnerar 5, en int, som bestämmer typen för fn.



I huvudfunktionen () finns påståendet:

bilvariab=fn(2);

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

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

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

#omfatta

använder sig av namnrymdtimmar;

tomhetandrafn(intnr 1,int (*ptr)(int))

{

intnr 2= (*ptr)(2);

kosta <<nr 1<< '' <<nr 2<< ' n';

}


inthuvud()

{

andrafn(4,[](intsluta)

{

intsvar=sluta+ 3;

lämna tillbakasvar;

});


lämna tillbaka 0;
}

Utgången är:

Fyra fem

Det finns två funktioner här, lambda -uttrycket och den andrafn () -funktionen. Lambda -uttrycket är det andra argumentet för det andrafn (), kallat i main (). Observera att lambda-funktionen (uttryck) inte slutar med ett semikolon i detta samtal eftersom det här är ett argument (inte en fristående funktion).

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

Påståendet,

intnr 2= (*ptr)(2);

I den andrafn () -definitionen kallar den lambda -funktionen med argumentet 2. Anropets returvärde, '(*ptr) (2)' från lambda -funktionen, tilldelas no2.

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

Delar av Lambda Expression

Delarna av en typisk lambda -funktion är följande:

[] () {}
  • [] är fångstklausulen. Den kan ha föremål.
  • () är för parameterlistan.
  • {} är för funktionskroppen. Om funktionen står ensam, bör den sluta 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 en parameter, en pekare till en funktion, som motsvarar lambda -funktionsdefinitionen.

Lambda -funktionsdefinitionen skiljer sig från den normala funktionsdefinitionen. Det kan tilldelas en variabel i det globala omfånget; denna funktion-tilldelade-till-variabel kan också kodas inuti en annan funktion. När den tilldelas en global omfattningsvariabel kan dess kropp se andra variabler i den globala omfattningen. 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ångstklausulens hjälp, [].

Capture-klausulen [], även känd som lambda-introducer, tillåter att variabler skickas från det omgivande (funktion) omfånget till lambda-uttryckets funktionskropp. Lambda -uttryckets 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 lambda -uttryckets funktionskropp. Följande program illustrerar detta, med funktionsomfånget (), som det omgivande omfånget:

#omfatta

använder sig av namnrymdtimmar;

inthuvud()

{

intid= 5;


bilfn= [id]()

{

kosta <<id<< ' n';

};

fn();


lämna tillbaka 0;

}

Utgången är 5 . Utan namnet, id, inuti [] skulle lambda -uttrycket inte ha sett variabel -id för funktionsomfånget main ().

Fånga med referens

Ovanstående exempel på användning av fångstklausulen är att fånga efter värde (se detaljer nedan). Vid fångst genom referens görs placeringen (lagringen) av variabeln, t.ex. id ovan, för det omgivande omfånget tillgängligt inuti lambda -funktionskroppen. Så att ändra värdet på variabeln inuti lambda -funktionskroppen kommer att ändra värdet på samma variabel i det omgivande omfånget. Varje variabel som upprepas i fångstklausulen föregås av ampersanden (&) för att uppnå detta. Följande program illustrerar detta:

#omfatta

använder sig av namnrymdtimmar;

inthuvud()

{

intid= 5; flytamed= 2.3; rödingkap= 'TILL';

bilfn= [&id,&med,&kap]()

{

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

};

fn();

kosta <<id<< ',' <<med<< ',' <<kap<< ' n';

lämna tillbaka 0;

}

Utgången är:

6, 3.4, B

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

Fånga efter värde

Vid fångst efter värde görs en kopia av variabelns plats, av det omgivande omfånget tillgängligt inuti lambda -funktionskroppen. Även om variabeln inuti lambda -funktionskroppen ä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 av något. Följande program illustrerar detta:

#omfatta

använder sig av namnrymdtimmar;

inthuvud()

{

intid= 5; flytamed= 2.3; rödingkap= 'TILL';

bilfn= [id, ft, ch]()

{

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

kosta <<id<< ',' <<med<< ',' <<kap<< ' n';

};

fn();

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

kosta <<id<< ',' <<med<< ',' <<kap<< ' n';

lämna tillbaka 0;

}

Utgången är:

5, 2.3, A

6, 3.4, B

Om kommentarindikatorn tas bort kommer programmet inte att kompilera. Kompilatorn kommer att ge ett felmeddelande om att variablerna i funktionskroppens definition av lambda -uttrycket inte kan ändras. Även om variablerna inte kan ändras inuti lambda -funktionen, kan de ändras utanför lambda -funktionen, som ovanstående programmets utdata visar.

Mixing Captures

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

#omfatta

använder sig av namnrymdtimmar;

inthuvud()

{

intid= 5; flytamed= 2.3; rödingkap= 'TILL'; boolbl= Sann;


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

{

kap= 'B';bl= falsk;

kosta <<id<< ',' <<med<< ',' <<kap<< ',' <<bl<< ' n';

};

fn();


lämna tillbaka 0;

}

Utgången är:

5, 2.3, B, 0

När alla fångats, är som referens:

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

#omfatta

använder sig av namnrymdtimmar;

inthuvud()

{

intid= 5; flytamed= 2.3; rödingkap= 'TILL'; boolbl= Sann;


bilfn= [&]()

{

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

};

fn();

kosta <<id<< ',' <<med<< ',' <<kap<< ',' <<bl<< ' n';


lämna tillbaka 0;

}

Utgången är:

6, 3.4, B, 0

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

använder sig av namnrymdtimmar;

inthuvud()

{

intid= 5; flytamed= 2.3; rödingkap= 'TILL'; boolbl= Sann;


bilfn= [&, id, ft]()

{

kap= 'B';bl= falsk;

kosta <<id<< ',' <<med<< ',' <<kap<< ',' <<bl<< ' n';

};

fn();


lämna tillbaka 0;

}

Utgången är:

5, 2.3, B, 0

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

När alla fångats, är efter värde:

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

#omfatta

använder sig av namnrymdtimmar;

inthuvud()
{

intid= 5; flytamed= 2.3; rödingkap= 'TILL'; boolbl= Sann;


bilfn= [=]()

{

kosta <<id<< ',' <<med<< ',' <<kap<< ',' <<bl<< ' n';

};

fn();


lämna tillbaka 0;


}

Utgången är:

5, 2.3, A, 1

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

Om vissa variabler ska fångas med värde och andra genom referens, representerar en = alla de skrivskyddade kopierade variablerna, och resten kommer att ha &, som följande program visar:

#omfatta

använder sig av namnrymdtimmar;

inthuvud()

{

intid= 5; flytamed= 2.3; rödingkap= 'TILL'; boolbl= Sann;


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

{

kap= 'B';bl= falsk;

kosta <<id<< ',' <<med<< ',' <<kap<< ',' <<bl<< ' n';

};

fn();


lämna tillbaka 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 lambda -uttrycket:

#omfatta

använder sig av namnrymdtimmar;

röding *produktion;


bilcba= [](rödingut[])

{

produktion=ut;

};



tomhetmainFunc(rödinginmatning[],tomhet (*för)(röding[]))

{

(*för)(inmatning);

kosta<<'för huvudfunktion'<<' n';

}


tomhetfn()

{

kosta<<'Nu'<<' n';

}


inthuvud()

{

rödinginmatning[] = 'för återuppringningsfunktion';

mainFunc(input, cba);

fn();

kosta<<produktion<<' n';



lämna tillbaka 0;

}

Utgången är:

för huvudfunktionen

Nu

för återuppringningsfunktion

Kom ihåg att när en lambda -uttrycksdefinition 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 lambda -uttryck är auto, vilket betyder att kompilatorn bestämmer returtypen från returuttrycket (om det finns). Om programmeraren verkligen vill ange returtyp, kommer han att göra det som i följande program:

#omfatta

använder sig av namnrymdtimmar;

bilfn= [](intsluta) -> int

{

intsvar=sluta+ 3;

lämna tillbakasvar;

};


inthuvud()

{

bilvariab=fn(2);

kosta <<variab<< ' n';


lämna tillbaka 0;

}

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

Stängning

Tänk på följande kodsegment:

strukturCla

{

intid= 5;

rödingkap= 'till';

}obj1, obj2;

Här är Cla namnet på strukturklassen. Obj1 och obj2 är två objekt som kommer att instansieras från strukturklassen. Lambda -uttryck liknar implementeringen. Lambdafunktionsdefinitionen är en slags klass. När lambda -funktionen kallas (anropas), instansieras ett objekt från dess definition. Detta objekt kallas förslutning. Det är stängningen som gör det arbete lambda förväntas göra.

Emellertid kommer kodning av lambda -uttrycket som strukturen ovan att ha obj1 och obj2 ersatt av motsvarande parameters argument. Följande program illustrerar detta:

#omfatta

använder sig av namnrymdtimmar;

bilfn= [](intparam1,intparam2)

{

intsvar=param1+param2;

lämna tillbakasvar;

} (2,3);


inthuvud()

{

bilvar=fn;

kosta <<var<< ' n';


lämna tillbaka 0;

}

Utdata är 5. Argumenten är 2 och 3 inom parentes. Observera att lambda -uttrycksfunktionsanropet, fn, inte tar något argument eftersom argumenten redan har kodats i slutet av lambda -funktionsdefinitionen.

Slutsats

Lambda -uttrycket ä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 lambda förväntas göra.

För att lambda-uttrycket ska kunna ta emot en variabel från ett yttre funktionsomfång behöver det en icke-tom fångstklausul i dess funktionskropp.