Syntaxe de base

En C++ il existe le concept de lambda. Ce sont des fonctions que l’on peut stocker dans des variables. Leur syntaxe est la suivante :

  • entre crochet, des variables à capturer ou à copier
  • entre parenthèses, ses paramètres
  • entre accolades, son corps.

On peut stocker les lambdas dans des variables/champs/paramètres de type std::function<type_de_retour(type_de_param_1, type_de_param_2, etc.)>.

std::function est décrit dans <functional>

#include <functional>
int main(void)
{
	std::function<void(std::string)> my_lambda =
		[](std::string str){std::cout << str << "\n";};
	my_lambda("hello");
}

Valeur de retour

Les lambdas peuvent renvoyer une valeur, tout comme une fonction. Il suffit de préciser son type entre les paramètre et le corps de la lambda en utilisant la syntaxe -> type.

int main(void)
{
    std::string foo{"string from the main"};
    std::function<int(void)> my_lambda = [&](void) -> int {
        return foo.length();
    };
    std::cout << my_lambda() << std::endl;
}

Captures

Copie

Une lambda peut “capturer” des variables de l’étendue dans laquelle est a été crée. On peut les capturer par copie en écrivant simplement leur nom entre crochets dans l’implémentation de la lambda.

int main(void)
{
    std::string foo{"string from the main"};

    std::function<void(void)> my_lambda = [foo](void) {
        std::cout << foo << "\n";
    };
    my_lambda();
}

Par défaut, les lambda ne peuvent pas modifier les variables qu’elles ont copier. Tenter de modifier foo dans le corps de la lambda provoquerai une erreur de compilation.

Pour que la lambda puisse modifier les variables capturées par copie, il faut la marquer mutable.

int main(void)
{
    std::string foo{"string from the main"};

    std::function<void(void)> my_lambda = [foo](void) mutable {
        foo[0] = ' ';
        std::cout << foo << "\n";
    };
    my_lambda();
    std::cout << foo << "\n";
}

Sortie :

 tring from the main
string from the main

On constate que la chaîne originale est intacte, ce qui nous montre que la lambda travaillait bien sur une copie.

Référence

Les lambda peuvent capturer des références vers des variables de l’étendues dans laquelle elles ont été crées.

Pour capturer une variable par référence, il faut donner son nom précédé d’un caractère & dans les crochets de l’implémentation de la lambda.

int main(void)
{
    std::string foo{"string from the main"};

    std::function<void(void)> my_lambda = [&foo](void) {
        foo[0] = ' ';
        std::cout << foo << "\n";
    };
    my_lambda();
    std::cout << foo << "\n";
}

Sortie :

 tring from the main
 tring from the main

On remarque que la lambda a bien modifié la variable originale.

On constate aussi qu’une lambda n’a pas besoin d’être mutable pour pouvoir modifier une variable référencée.

Enfin, il est possible de capturer TOUTES les variables de l’étendue en ne mettant que le caractère & entre crochets :

int main(void)
{
    std::string foo{"string from the main"};
    int bar = 13;

    std::function<void(void)> my_lambda = [&](void) {
        foo[0] = ' ';
        std::cout << foo << "\n";
        bar = 12;
        std::cout << bar << "\n";
    };
    my_lambda();
    std::cout << foo << "\n";
}