User Tools

Site Tools


cpp:syntax:lambda

Les fonctions anonymes en C++

Pourquoi les fonctions anonymes

Souvent nous aimerions pouvoir paramètrer une fonction par un élément de code. Imaginons une fonction qui prendre un tableau d'éléments qui a pour type T et souhaite appliquer à ces éléments une opération de transformation qui prendrait un élément de type T et le convertirait en élément de type T2.

Nous pouvons imaginer une telle fonction générique en utilisant des objets.

Nous définissons la fonction map comme suit :

template<class T1, class T2, class transformT>
std::vector<T2> map(const std::vector<T1>& vec, transformT aFunction)
{
  std::vector<T2> result;
  result.reserve(vec.size());
  for(int i = 0; i < vec.size(); i++)
      result.push_back(aFunction(vec[i]);    
}

Nous pouvons donc définir la transformation suivante

struct multiplyBy2
{
    int operator(int aValue) { return aValue * 2; }
};

Et dès lors :

  std::vector<int> vect = {1, 2, 3, 4, 5 };
  std::vector<int> vectMultiplyBy2 = map(vect, multiplyBy2());

appellera pour chacun des éléments de vect la function int operator(int aValue) de l'objet multiplyBy2.

Donc nous pouvons encapsuler des fonctions dans des objets et utiliser ces objets pour passer du code à exécuter à une autre fonction. Cependant, ce n'est pas très lisible et intuitif.

Les fonctions anonymes en C++

C++ a introduit avec la version C++11 une syntaxe pour faire le travail précédent à votre place. En fait, le fonctionnement est toujours le même que celui précédemment décrit mais c'est le compilateur qui fait le travail et non plus vous.

Donc quand j'écris la fonction map, je vais pouvoir écrire toujours la fonction de la même manière, mais je vais passer en paramètre non plus un constructeur sur l'objet qui implante la fonction mais directement la fonction :

template<class T1, class T2, class transformT>
std::vector<T2> map(const std::vector<T1>& vec, transformT aFunction)
{
  std::vector<T2> result;
  result.reserve(vec.size());
  for(int i = 0; i < vec.size(); i++)
      result.push_back(aFunction(vec[i]);    
}
 
...
  std::vector<int> vect = {1, 2, 3, 4, 5 };
  std::vector<int> vectMultiplyBy2 = map(vect, [](int x) { return x * 2; });
...

Une fonction anonyme se compose donc de trois parties :

  • [] : il s'agit de la liste des variables auxquelles on souhaite accéder. Par défaut, dans le corps d'une fonction, nous n'accédons qu'aux variables locales à la fonction et aux paramètres. Cependant, il est possible d'accéder aux variables dans le contexte d'appel.
  • (int x) : il s'agit de la liste des paramètres de la fonction.
  • { return x * 2; } : il s'agit du corps de la fonction qui contient le code de la fonction qui sera exécuté.

Et voilà. C'est le compilateur qui fait le reste du travail…

Maintenant à quoi sert la liste [].

...
  std::vector<int> vect = {1, 2, 3, 4, 5 };
  int bound = 3;
  std::vector<int> vectModuloValue = map(vect, [bound](int x) { return x % bound; });
...

Cela sert à indiquer que l'on souhaite accéder à la valeur bound qui a été définie et qui est un paramètre de la fonction. Si bound n'était pas indiqué dans la liste des paramètres, le code ne pourrait pas compiler parce que bound est défini à l'exérieur de la fonction. Quand vous indiquez que vous allez utiliser bound, le compilateur va considérer que bound est lié à la fonction.

cpp/syntax/lambda.txt · Last modified: 2022/11/18 10:47 (external edit)