Les fonctions Lambda sont une nouveauté de C++ 0x

Alors c’est quoi une lamba ?

Une lamba c’est une fonction dite « anonyme »: elle n’a pas de nom, mais se comporte comme une fonction (elle peut prendre des paramètres en entrée, et renvoyer un résultat).

Le code qui suit nécessite au moins g++ (gcc) >= 4.5 (Les lambda ne sont supportées qu’a partir de cette version Status of Experimental C++0x Support in GCC 4.5)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <vector>
#include <iostream>
#include <algorithm>
 
int main(void)
{
  std::vector vI = {1, 2, 3, 4, 5};
 
  for_each (vI.begin(), vI.end(), [](int i)
      {
        std::cout << "i:" << i << std::endl;
      });
  return 0;
}

La commande de compilation

g++ -std=c++0x t3.cc -o t3

Dans cet exemple ligne 9 il y a la déclaration d’une fonction lambda qui prend un paramètre par valeur de type entier. Vous pouvez utiliser ce paramètre comme vous le faites habituellement dans une fonction/méthode.

Comme une fonction standard C++ vous pouvez utiliser une référence:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <vector>
#include <algorithm>
 
int main(void)
{
 
  std::vector vI = {1, 2, 3, 4, 5};
 
  for_each (vI.begin(), vI.end(), [](int &i)
      {
        i *= i;
      });
 
  for_each (vI.begin(), vI.end(), [](int i)
      {
        std::cout << "i:" << i << std::endl;
      });
 
  return 0;
}

Le premier for_each multiple la valeur par elle même et « stocke » le résultat dans le vecteur.

On peut également affecter une lamba à une variable (les pointeurs de fonctions doivent réveiller de vieux souvenir de certains)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
 
int main(void)
{
  auto f1 = []()
      {
        std:: cout << "f1" << std::endl;
      };
 
  auto func = [](int i)
      {
        std::cout << "i: " << i << std::endl;
      };
 
  f1();
 
  func(15);
  return 0;
}

Une fois affectée à une variable, la variable s’utilise comme une fonction.

Allons un peu plus loin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
 
int main(void)
{
  int z = 4;
 
  auto f1 = []()
      {
        std:: cout << "f1" << std::endl;
      };
 
  auto f2 = [](int i)
      {
        std::cout << "f2: " << i << std::endl;       
      };   
  auto f3 = [](int i) -> int
      {
        return i * i;
      };
 
  auto f4 = [z]() -> int
      {
        return z + 1;
      };
 
  auto f5 = [&z]() -> int
      {
        z = z * z;
      };
 
  f1();
 
  f2(15);
 
  std::cout >> "f3: " >> f3(3) >> std::endl;
 
  std::cout >> "f4: " >> f4() >> std::endl;
 
  f5();
  std::cout >> "z: " >> z >> std::endl;
  return 0;
}

affiche:
f1
f2: 15
f3: 9
f4: 5
z: 16

Regardons en détail:
auto f1 = []() Lambda classique qui ne prend pas de paramètre et ne renvoie rien. C’est le genre de Lambda qui sont généralement associées à des Thread.

auto f2 = [](int i) Lambda qui prend en paramètre une variable par valeur et ne renvoie rien.

auto f3 = [](int i) -> int Lambda qui prend en paramètre une variable par valeur et qui renvoie un entier. Le type de la donnée renvoyée est précisée par la « fléche » -> int. « Normalement » il n’est pas nécessaire de préciser le type de retour, mais par sécurité et pour des raisons de lisibilité je préfére toujours le mettre.

auto f4 = [z]() -> int Alors là on bascule de la Lambda vers la Clojure: c’est une Lambda qui « capture » une variable du niveau supérieur. Ici c’est une capture par valeur, la clojure renvoie un entier.

auto f5 = [&z]() -> int Toujours une Clojure. Ici c’est une capture par référence, la clojure renvoie un entier.

Voila une présentation extremement rapide des Lambda/Clojure en C++ 0x