Wat is een lambda-expressie in C++ 11?

Wat is een Lambda-expressie in C++ 11? Wanneer zou ik er een gebruiken? Welke klasse probleem oplossen ze dat niet mogelijk was voorafgaand aan hun introductie?

Een paar voorbeelden en gebruiksgevallen zouden nuttig zijn.


1, Autoriteit 100%

het probleem

C++ Inclusief bruikbare generieke functies zoals std::for_eachen std::transform, wat erg handig kan zijn. Helaas kunnen ze ook behoorlijk omslachtig zijn om te gebruiken, vooral als de FUTRUCTOR U zou willen Toepassen is uniek voor de specifieke functie.

#include <algorithm>
#include <vector>
namespace {
  struct f {
    void operator()(int) {
      // do something
    }
  };
}
void func(std::vector<int>& v) {
  f f;
  std::for_each(v.begin(), v.end(), f);
}

Als u alleen fgebruikt en op die specifieke plaats lijkt het overkill om een ​​hele klas te schrijven om iets triviaal en één uit te doen.

In C++ 03 bent u misschien in de verleiding om iets als het volgende te schrijven, om de FUNCTOR LOKAL te houden:

void func2(std::vector<int>& v) {
  struct {
    void operator()(int) {
       // do something
    }
  } f;
  std::for_each(v.begin(), v.end(), f);
}

Dit is echter niet toegestaan, fkan niet worden doorgegeven aan een Sjabloon functie in C++ 03.

de nieuwe oplossing

C++11 introduceert lambda’s waarmee je een inline, anonieme functor kunt schrijven om de struct fte vervangen. Voor kleine eenvoudige voorbeelden kan dit schoner zijn om te lezen (het houdt alles op één plaats) en mogelijk eenvoudiger te onderhouden, bijvoorbeeld in de eenvoudigste vorm:

void func3(std::vector<int>& v) {
  std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

Lambda-functies zijn slechts syntactische suikers voor anonieme functors.

Retourtypes

In eenvoudige gevallen wordt het retourtype van de lambda voor u afgeleid, bijvoorbeeld:

void func4(std::vector<double>& v) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [](double d) { return d < 0.00001 ? 0 : d; }
                 );
}

wanneer u echter complexere lambda’s begint te schrijven, zult u snel gevallen tegenkomen waarin het retourtype niet door de compiler kan worden afgeleid, bijvoorbeeld:

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

Om dit op te lossen mag je expliciet een retourtype specificeren voor een lambda-functie, met behulp van -> T:

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) -> double {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

Variabelen “vastleggen”

Tot nu toe hebben we niets anders gebruikt dan wat erin is doorgegeven aan de lambda, maar we kunnen ook andere variabelen gebruiken, binnen de lambda. Als u toegang wilt tot andere variabelen, kunt u de capture-clausule gebruiken (de []van de expressie), die tot nu toe in deze voorbeelden niet is gebruikt, bijvoorbeeld:

void func5(std::vector<double>& v, const double& epsilon) {
    std::transform(v.begin(), v.end(), v.begin(),
        [epsilon](double d) -> double {
            if (d < epsilon) {
                return 0;
            } else {
                return d;
            }
        });
}

U kunt vastleggen op zowel referentie als waarde, die u kunt specificeren met respectievelijk &en =:

  • [&epsilon]legt vast op referentie
  • [&]legt alle variabelen vast die in de lambda worden gebruikt als referentie
  • [=]legt alle variabelen vast die in de lambda worden gebruikt op waarde
  • [&, epsilon]legt variabelen vast zoals bij [&], maar epsilon op waarde
  • [=, &epsilon]legt variabelen vast zoals bij [=], maar epsilon als referentie

De gegenereerde operator()is standaard const, met de implicatie dat het vastleggen constzal zijn wanneer u ze standaard opent. Dit heeft tot gevolg dat elke aanroep met dezelfde invoer hetzelfde resultaat zou opleveren, maar u kunt markeer de lambda als mutableom te verzoeken dat de geproduceerde operator()niet const.


Antwoord 2, autoriteit 54%

Wat is een Lambda-functie?

Het C++ -concept van een Lambda-functie is afkomstig in de Lambda-calculus en functionele programmering. Een lambda is een naamloze functie die handig is (in de daadwerkelijke programmering, geen theorie) voor korte fragmenten van de code die onmogelijk zijn om opnieuw te gebruiken en niet de moeite waard te zijn.

In C++ Een Lambda-functie wordt als volgt gedefinieerd

[]() { } // barebone lambda

of in al zijn glorie

[]() mutable -> T { } // T is the return type, still lacking throw()

[]is de opnamelijst, ()de argumentlijst en {}de functie-instantie.

De opnamelijst

De opnameslijst definieert wat vanaf de buitenkant van de lambda beschikbaar moet zijn in de functiegeldigheid en hoe.
Het kan ook:

  1. een waarde: [x]
  2. een referentie [& amp; x]
  3. Elke variabele die momenteel in reikwijdte door verwijzing [& amp;]
  4. hetzelfde als 3, maar op waarde [=]

U kunt een van de bovenstaande mixen in een door komma’s gescheiden lijst [x, &y].

de argumentlijst

De argumentlijst is hetzelfde als in een andere C++ -functie.

De functie Body

De code die wordt uitgevoerd wanneer de Lambda eigenlijk wordt genoemd.

Retourtype aftrek

Als een Lambda slechts één retourverklaring heeft, kan het retourtype worden weggelaten en heeft het impliciete type decltype(return_statement).

MUBABEL

Als een lambda gemarkeerd is (b.v. []() mutable { }) mag deze de waarden die op waarde zijn vastgelegd, muteren.

Gebruiksgevallen

De bibliotheek gedefinieerd door de ISO-standaard profiteert sterk van lambda’s en verhoogt de bruikbaarheid met verschillende bars, omdat gebruikers nu hun code niet vol hoeven te proppen met kleine functors in een toegankelijk bereik.

C++14

In C++14 zijn lambda’s uitgebreid met verschillende voorstellen.

Geïnitialiseerde Lambda-opnames

Een element van de opnamelijst kan nu worden geïnitialiseerd met =. Dit maakt het mogelijk om variabelen te hernoemen en vast te leggen door te bewegen. Een voorbeeld uit de standaard:

int x = 4;
auto y = [&r = x, x = x+1]()->int {
            r += 2;
            return x+2;
         }();  // Updates ::x to 6, and initializes y to 7.

en een van Wikipedia die laat zien hoe je vastlegt met std::move:

auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};

Generieke Lambda’s

Lambda’s kunnen nu generiek zijn (autozou hier gelijk zijn aan Tals
Twaren een type sjabloonargument ergens in het omringende bereik):

auto lambda = [](auto x, auto y) {return x + y;};

Verbeterde aftrek van retourtype

C++14 staat afgeleide retourtypen toe voor elke functie en beperkt het niet tot functies van de vorm return expression;. Dit wordt ook uitgebreid tot lambda’s.


Antwoord 3, autoriteit 10%

Lambda-expressies worden doorgaans gebruikt om algoritmen in te kapselen, zodat ze kunnen worden doorgegeven aan een andere functie. het is echter mogelijk om direct na definitie een lambda uit te voeren:

[&](){ ...your code... }(); // immediately executed lambda expression

is functioneel gelijk aan

{ ...your code... } // simple code block

Dit maakt lambda-expressies een krachtig hulpmiddel voor het herstructureren van complexe functies. U begint met het inpakken van een codesectie in een lambda-functie zoals hierboven weergegeven. Het proces van expliciete parametrering kan dan geleidelijk worden uitgevoerd met tussentijdse testen na elke stap. Zodra je het codeblok volledig hebt geparametriseerd (zoals aangetoond door het verwijderen van de &), kun je de code naar een externe locatie verplaatsen en er een normale functie van maken.

Op dezelfde manier kunt u lambda-expressies gebruiken om variabelen te initialiseren op basis van het resultaat van een algoritme

int a = []( int b ){ int r=1; while (b>0) r*=b--; return r; }(5); // 5!

Als een manier om je programmalogica te partitioneren, zou het zelfs handig kunnen zijn om een lambda-expressie als argument door te geven aan een andere lambda-expressie…

[&]( std::function<void()> algorithm ) // wrapper section
   {
   ...your wrapper code...
   algorithm();
   ...your wrapper code...
   }
([&]() // algorithm section
   {
   ...your algorithm code...
   });

Met

Lambda-expressies kun je ook met de naam geneste functiesmaken, die kan een handige manier zijn om dubbele logica te vermijden. Het gebruik van benoemde lambda’s is ook een beetje gemakkelijker voor de ogen (vergeleken met anonieme inline lambda’s) bij het doorgeven van een niet-triviale functie als parameter aan een andere functie. Opmerking: vergeet de puntkomma na de accolade sluiten niet.

auto algorithm = [&]( double x, double m, double b ) -> double
   {
   return m*x+b;
   };
int a=algorithm(1,2,3), b=algorithm(4,5,6);

Als latere profilering aanzienlijke initialisatie-overhead voor het functie-object aan het licht brengt, kunt u ervoor kiezen om dit te herschrijven als een normale functie.


Antwoord 4, autoriteit 3%

Antwoorden

V: Wat is een lambda-expressie in C++11?

A: Onder de motorkap is het het object van een automatisch gegenereerde klasse met overbelastende operator() const. Een dergelijk object wordt afsluitinggenoemd en is gemaakt door de compiler.
Dit ‘sluiting’-concept komt in de buurt van het bind-concept uit C++11.
Maar lambda’s genereren doorgaans betere code. En oproepen via sluitingen maken volledige inlining mogelijk.

V: Wanneer zou ik er een gebruiken?

A: Om “eenvoudige en kleine logica” te definiëren en de compiler te vragen het genereren van de vorige vraag uit te voeren. Je geeft een compiler enkele expressies die je binnen operator() wilt hebben. Alle andere dingen die de compiler voor u genereert.

V: Welke soort problemen lossen ze op die vóór hun introductie niet mogelijk waren?

A: Het is een soort syntaxis, zoals operators die overbelasten in plaats van functies voor aangepaste add, subrtact-bewerkingen… Maar het bespaart meer regels onnodige code om 1-3 regels real logica voor sommige klassen, en etc.! Sommige ingenieurs denken dat als het aantal regels kleiner is, er minder kans is om er fouten in te maken (ik denk ook van wel)

Voorbeeld van gebruik

auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);

Extra’s over lambda’s, niet gedekt door een vraag. Negeer deze sectie als je niet geïnteresseerd bent

1. Gevangen waarden. Wat u kunt vastleggen

1.1. U kunt verwijzen naar een variabele met statische opslagduur in lambdas. Ze zijn allemaal gevangen.

1.2. U kunt lambda gebruiken voor het vastleggen van waarden “op waarde”. In dat geval worden vastgelegde vars gekopieerd naar het functie-object (sluiting).

[captureVar1,captureVar2](int arg1){}

1.3. U kunt vastleggen als referentie. & — in deze context bedoelen we verwijzingen, geen verwijzingen.

  [&captureVar1,&captureVar2](int arg1){}

1.4. Er bestaat een notatie om alle niet-statische var’s op waarde of op referentie vast te leggen

 [=](int arg1){} // capture all not-static vars by value
  [&](int arg1){} // capture all not-static vars by reference

1.5. Er bestaat een notatie om alle niet-statische var’s op waarde of op referentie vast te leggen en iets te specificeren. meer.
Voorbeelden:
Leg alle niet-statische var’s vast op waarde, maar leg op basis van referentie vast Param2

[=,&Param2](int arg1){} 

Leg alle niet-statische var’s vast door middel van referentie, maar door waarde vast te leggen Param2

[&,Param2](int arg1){} 

2. Retour type aftrek

2.1. Lambda-retourtype kan worden afgeleid als lambda één uitdrukking is. Of je kunt het expliciet specificeren.

[=](int arg1)->trailing_return_type{return trailing_return_type();}

Als lambda meer dan één expressie heeft, moet het retourtype worden opgegeven via het achterste retourtype.
Een vergelijkbare syntaxis kan ook worden toegepast op automatische functies en lidfuncties

3. Gevangen waarden. Wat u niet kunt vastleggen

3.1. U kunt alleen lokale vars vastleggen, geen lidvariabele van het object.

4. Сonversies

4.1 !! Lambda is geen functieaanwijzer en het is geen anonieme functie, maar capture-lesslambda’s kunnen impliciet worden omgezet in een functieaanwijzer.

ps

  1. Meer over lambda-grammatica-informatie is te vinden in Working draft for Programming Language C++ #337, 2012-01-16, 5.1.2. Lambda-expressies, p.88

  2. In C++14 is de extra functie met de naam “init capture” toegevoegd. Het staat toe om willekeurig de leden van sluitingsgegevens uit te voeren:

    auto toFloat = [](int value) { return float(value);};
    auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};
    

Antwoord 5

Een Lambda-functie is een anonieme functie die u in-line maakt. Het kan variabelen vastleggen zoals sommigen hebben uitgelegd, (bijv. http://www.struprup.com/c++11faq .html # lambda ) maar er zijn enkele beperkingen. Bijvoorbeeld, als er zo’n callback-interface is,

void apply(void (*f)(int)) {
    f(10);
    f(20);
    f(30);
}

U kunt ter plaatse een functie schrijven om het te gebruiken zoals die is gepasseerd om hieronder toe te passen:

int col=0;
void output() {
    apply([](int data) {
        cout << data << ((++col % 10) ? ' ' : '\n');
    });
}

Maar u kunt dit niet doen:

void output(int n) {
    int col=0;
    apply([&col,n](int data) {
        cout << data << ((++col % 10) ? ' ' : '\n');
    });
}

Vanwege beperkingen in de C++ 11-standaard. Als u Captures wilt gebruiken, moet u op de bibliotheek en

vertrouwen

#include <functional> 

(of een andere STL-bibliotheek zoals algoritme om het indirect te krijgen) en vervolgens met STD :: -functie werken in plaats van normale functies als parameters zoals deze:

#include <functional>
void apply(std::function<void(int)> f) {
    f(10);
    f(20);
    f(30);
}
void output(int width) {
    int col;
    apply([width,&col](int data) {
        cout << data << ((++col % width) ? ' ' : '\n');
    });
}

Antwoord 6

De lambda’s in c++ worden behandeld als “on the go beschikbare functie”.
ja het is letterlijk onderweg, jij definieert het; gebruik het; en als het bereik van de ouderfunctie is voltooid, is de lambda-functie verdwenen.

c++ introduceerde het in c++ 11 en iedereen begon het op elke mogelijke plaats te gebruiken.
het voorbeeld en wat lambda is, vind je hier https://en.cppreference.com/ w/cpp/taal/lambda

ik zal beschrijven wat er niet is, maar essentieel is om te weten voor elke c++ programmeur

Lambda is niet bedoeld om overal te gebruiken en elke functie kan niet worden vervangen door lambda. Het is ook niet de snelste in vergelijking met de normale functie. omdat het wat overhead heeft die door lambda moet worden afgehandeld.

het zal in sommige gevallen zeker helpen bij het verminderen van het aantal regels.
het kan in principe worden gebruikt voor het gedeelte van de code dat een of meerdere keren in dezelfde functie wordt aangeroepen en dat stuk code is nergens anders nodig, zodat u er een zelfstandige functie voor kunt maken.

Hieronder staat het basisvoorbeeld van lambda en wat er op de achtergrond gebeurt.

Gebruikerscode:

int main()
{
  // Lambda & auto
  int member=10;
  auto endGame = [=](int a, int b){ return a+b+member;};
  endGame(4,5);
  return 0;
}

Hoe compileren het uitbreidt:

int main()
{
  int member = 10;
  class __lambda_6_18
  {
    int member;
    public: 
    inline /*constexpr */ int operator()(int a, int b) const
    {
      return a + b + member;
    }
    public: __lambda_6_18(int _member)
    : member{_member}
    {}
  };
  __lambda_6_18 endGame = __lambda_6_18{member};
  endGame.operator()(4, 5);
  return 0;
}

Zoals je kunt zien, wat voor soort overhead voegt het toe als je het gebruikt.
dus het is geen goed idee om ze overal te gebruiken.
het kan worden gebruikt op plaatsen waar ze van toepassing zijn.


Antwoord 7

Nou, een praktisch gebruik dat ik heb ontdekt, is het verminderen van de boilerplate-code. Bijvoorbeeld:

void process_z_vec(vector<int>& vec)
{
  auto print_2d = [](const vector<int>& board, int bsize)
  {
    for(int i = 0; i<bsize; i++)
    {
      for(int j=0; j<bsize; j++)
      {
        cout << board[bsize*i+j] << " ";
      }
      cout << "\n";
    }
  };
  // Do sth with the vec.
  print_2d(vec,x_size);
  // Do sth else with the vec.
  print_2d(vec,y_size);
  //... 
}

Zonder lambda moet je misschien iets doen voor verschillende bsizegevallen. Natuurlijk zou je een functie kunnen maken, maar wat als je het gebruik wilt beperken binnen de reikwijdte van de ziel-gebruikersfunctie? de aard van lambda voldoet aan deze eis en ik gebruik het voor dat geval.


Antwoord 8

Een probleem dat het oplost: Code eenvoudiger dan lambda voor een aanroep in de constructor die een uitvoerparameterfunctie gebruikt voor het initialiseren van een const-lid

Je kunt een const-lid van je klasse initialiseren met een aanroep van een functie die zijn waarde instelt door zijn uitvoer terug te geven als een uitvoerparameter.


Antwoord 9

C++ 11 introduceerde lambda-expressie zodat we een inline-functie kunnen schrijven die kan worden gebruikt voor korte codefragmenten

[ capture clause ] (parameters) -> return-type
{
   definition of method
}

Over het algemeen wordt het return-type in lambda-expressies geëvalueerd door de compiler zelf en dat hoeven we niet expliciet te specificeren en -> return-type kan worden genegeerd, maar in sommige complexe gevallen, zoals in een voorwaardelijke instructie, kan de compiler het return-type niet onderscheiden en dat moeten we specificeren.

// C++ program to demonstrate lambda expression in C++
#include <bits/stdc++.h>
using namespace std;
// Function to print vector
void printVector(vector<int> v)
{
    // lambda expression to print vector
    for_each(v.begin(), v.end(), [](int i)
    {
        std::cout << i << " ";
    });
    cout << endl;
}
int main()
{
    vector<int> v {4, 1, 3, 5, 2, 3, 1, 7};
    printVector(v);
    // below snippet find first number greater than 4
    // find_if searches for an element for which
    // function(third argument) returns true
    vector<int>:: iterator p = find_if(v.begin(), v.end(), [](int i)
    {
        return i > 4;
    });
    cout << "First number greater than 4 is : " << *p << endl;
    // function to sort vector, lambda expression is for sorting in
    // non-decreasing order Compiler can make out return type as
    // bool, but shown here just for explanation
    sort(v.begin(), v.end(), [](const int& a, const int& b) -> bool
    {
        return a > b;
    });
    printVector(v);
    // function to count numbers greater than or equal to 5
    int count_5 = count_if(v.begin(), v.end(), [](int a)
    {
        return (a >= 5);
    });
    cout << "The number of elements greater than or equal to 5 is : "
        << count_5 << endl;
    // function for removing duplicate element (after sorting all
    // duplicate comes together)
    p = unique(v.begin(), v.end(), [](int a, int b)
    {
        return a == b;
    });
    // resizing vector to make size equal to total different number
    v.resize(distance(v.begin(), p));
    printVector(v);
    // accumulate function accumulate the container on the basis of
    // function provided as third argument
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int f = accumulate(arr, arr + 10, 1, [](int i, int j)
    {
        return i * j;
    });
    cout << "Factorial of 10 is : " << f << endl;
    //   We can also access function by storing this into variable
    auto square = [](int i)
    {
        return i * i;
    };
    cout << "Square of 5 is : " << square(5) << endl;
}

Uitvoer

4 1 3 5 2 3 1 7
First number greater than 4 is : 5
7 5 4 3 3 2 1 1
The number of elements greater than or equal to 5 is : 2
7 5 4 3 2 1
Factorial of 10 is : 3628800
Square of 5 is : 25

Een lambda-expressie kan meer kracht hebben dan een gewone functie door toegang te hebben tot variabelen uit de omsluitende scope. We kunnen op drie manieren externe variabelen van het omsluitende bereik vastleggen:

  • Vastleggen op referentie
  • Vastleggen op waarde
  • Vastleggen door beide (gemengde opname)

De syntaxis die wordt gebruikt voor het vastleggen van variabelen:

  • [&] : leg alle externe variabelen vast met referentie
  • [=] : leg alle externe variabelen vast op waarde
  • [a, &b] : leg a vast op waarde en b op referentie
    Een lambda met een lege capture-clausule [ ] heeft alleen toegang tot die variabelen die er lokaal voor zijn.
   #include <bits/stdc++.h>
    using namespace std;
    int main()
    {
        vector<int> v1 = {3, 1, 7, 9};
        vector<int> v2 = {10, 2, 7, 16, 9};
        // access v1 and v2 by reference
        auto pushinto = [&] (int m)
        {
            v1.push_back(m);
            v2.push_back(m);
        };
        // it pushes 20 in both v1 and v2
        pushinto(20);
        // access v1 by copy
        [v1]()
        {
            for (auto p = v1.begin(); p != v1.end(); p++)
            {
                cout << *p << " ";
            }
        };
        int N = 5;
        // below snippet find first number greater than N
        // [N] denotes, can access only N by value
        vector<int>:: iterator p = find_if(v1.begin(), v1.end(), [N](int i)
        {
            return i > N;
        });
        cout << "First number greater than 5 is : " << *p << endl;
        // function to count numbers greater than or equal to N
        // [=] denotes, can access all variable
        int count_N = count_if(v1.begin(), v1.end(), [=](int a)
        {
            return (a >= N);
        });
        cout << "The number of elements greater than or equal to 5 is : "
            << count_N << endl;
    }

Uitgang:

  First number greater than 5 is : 7
   The number of elements greater than or equal to 5 is : 3

Other episodes