een lange titel

C++ Functors en lambdafuncties

Dit artikel legt uit hoe je de capture bij lambdafuncties moet begrijpen.

Zoals gewone functies kunnen lambdafuncties ook parameters krijgen. Dit de gangbare techniek om data naar een functie te kopiëren.

Bij lambdafuncties is er nog een extramechanisme en dat is de capture. Zoals het woord het zegt, kan een capture de stand van een aantal externe variabelen bevroren worden. Met extern wordt hier de lokale variabelen en parameters die buiten de lambdafunctie bestaan. De waarde van deze externe variabelen wordt vastgelegd voor gebruik binnen de lambdafunctie. Ook al worden de externe variabelen later gewijzigd, toch behouden ze hun waarde die ze hadden tijdens het vastleggen van de lambdafunctie. Om dit proces duidelijk te maken is een stap terug nodig.

We leggen dit uit met een functorklasse. Dit is een klasse met naast de constructor slechts één methode, namelijk operator(). Hierdoor kan een object van deze klassen als een functie gebruikt worden. Dit type object wordt functieobject of functor genoemd.

Dit is de klasse:

// functorklasse
class xyz12345
{
private:
   int y;

public:
   xyz12345(int yy) : y(yy)
   {
   }
	
   int operator()(int *p, int s)
   {
      return *p + s + y;
   }
};

Deze klasse is niet speciaal; er is een datamember y zodat in objecten van deze klasse één int opgeslagen worden. Deze opslag doet dienst als capture. Met de constructor kan een waarde in deze y opgeslagen worden. De operator() krijgt het adres van een int1 en nog een int en maakt de som van 3 waarden.

Dan maken we eerst een functorobject.

int w = 5;
xyz12345 obj(w);
~~~~

In het object obj wordt de waarde 5 opgeslagen. Eigenlijk doet obj een opslag (capture) van de momentele waarde van w. De opgeslagen waarde is 5. w mag achteraf nog gewijzigd worden; dit heeft geen invloed op de 5 die nu al in obj opgeslagen is.

In het volgende fragment wordt de functie van het functieobject opgeroepen. Hierbij worden twee parameters meegegeven. De eerste waarde is 7 en de tweede is 3. Intern wordt de som 7+3+5 berekend en als resultaat teruggegeven.

int s9 = 7;
s9 = obj(&s9, 3);
std::cout << "s9 " << s9 << "\n";

Het volgende fragment doet identiek hetzelfde maar nu met een lambdafunctie.

auto la =
[w](int *p, int s)
{
   return *p + s + w; 
};
s9 = la(&s9, 3);

Het voordeel van de lambdafunctie is dat er een syntax met [] bestaat voor het vastleggen van de capture. Deze syntax is nieuw sinds C++11 en zal meer en meer opduiken in situaties zoals eventafhandeling in Qt.


  1. Waarom dit een pointer naar int is en geen int doet hier niet terzake. [return]