Les fonctions Lambda en C++

18 Nov 2021 18 Nov 2021 4410 vues ESSADDOUKI Mostafa 5 min de lecture
Introduction et syntaxe de base
1 Introduction au langage C++ 2 Entrée-sortie en C++ - cin et cout 3 Inférence de type avec le mot-clé auto en C++ 4 Classe std::string et les chaînes de caractères en C++ 5 Les structures conditionnelles (if et switch) en C++ (C++17 et C++20) 6 Les boucles en C++ (C++17 et C++20) 7 La gestion des fichiers en C++
Pointeurs et fonctions
8 Introduction aux pointeurs en C++ - Déclaration et interêts 9 Les références en C++ - déclaration et interêts 10 Les tableaux en C++ - Déclaration et interêts 11 Introduction aux fonctions en C++ 12 Passer des arguments à une fonction en C++ 13 Déclarer un paramètre const en C++ 14 Les fonctions Lambda en C++ 15 Fonctions utiles (Mathématiques et caractères) en C++
Programmation OO
16 Classes et objets en C++ 17 Spécificateurs d'accès en C++ 18 Constructeurs et destructeur d'une classe en C++ 19 Fonctions membres en C++ 20 Membres statiques d'une classe en C++ 21 Fonctions en ligne en C++ - inline 22 Fonctions et classes amies en C++ - friend 23 Surcharge des fonctions en C++ 24 Surcharge des opérateurs en C++ 25 Héritage en C++ 26 La gestion d'exceptions en C++ : déclaration, utilisation et personnalisation 27 fonctions et classes templates en C++ 28 Les nouveautés C++20 pour améliorer les templates en C++
Structures de données
29 Introduction aux structures de données 30 Les structures en C++ et la différence avec les structures en C 31 Les listes chaînées en C++ 32 Les piles en C++ 33 File d'attente en C++ 34 Arbre binaire de recherche : définition et mise en oeuvre en C++
La bibliothèque standard (STL)
35 Introduction à la bibliothèque de Template Standard STL 36 Les itérateurs en C++ - définition, déclaration et exemples 37 La classe array en C++ (bibliothèque STL) <array> 38 La classe vector de la bibliothèque STL <vector> 39 La classe deque en C++ ( Bibliothèque STL) 40 La classe list en C++ (bibliothèque STL) <list> 41 La classe stack (Pile) en C++ (bibliothèque STL) <stack> 42 La classe queue (File d'attente) en C++ (bibliothèque STL) <queue> 43 La file d'attente prioritaire (classe priority_queue) - Bibliothèque STL 44 Les ensembles en C++ (Classe set <set> - Bibliothèque STL) 45 Les dictionnaires en C++ : Classe map (Bibliothèque STL) 46 Introduction aux algorithmes de la bibliothèque STL (programmation compétitive) 47 Tri et méthodes associées en C++ - Bibliothèque STL 48 Recherche dichotomique et méthodes associées en C++ - Bibliothèque STL 49 Appliquer un prédicat ou une fonction aux éléments d'une séquence en C++ - Bibliothèque STL 50 Recherche dans une séquence et méthodes associées en C++ - Bibliothèque STL

Fonctions Lambda en C++

En C++, nous pouvons utiliser des fonctions sans nom appelées fonctions lambda. Une telle fonction est un objet d'un type spécial qui peut être affecté à une variable, laquelle peut ensuite être appelée comme une fonction ordinaire.

Une fonction lambda est une construction sémantique qui détermine une fonction. Sa syntaxe est la suivante :

   
Syntaxe C++
[capture](parametres) -> return_type {
    // instructions
};

La syntaxe commence par une clause de capture [], suivie des paramètres entre parenthèses. Après la liste des paramètres, on indique le type de retour avec la flèche ->, puis les instructions entre accolades.

Simplifications possibles
  • Si le compilateur peut déduire le type de retour, la flèche -> et le type peuvent être omis.
  • Si la fonction lambda n'a pas de paramètres, les parenthèses peuvent également être omises.
  • Les fonctions lambda qui ne capturent aucune variable externe sont dites sans état (stateless).
Mot-clé auto Le mot-clé auto permet de définir le type d'une variable automatiquement. Comme une fonction lambda appartient à un type anonyme (sans nom accessible), on utilise auto pour déclarer la variable qui la reçoit.

Exemples de fonctions lambda sans capture

L'instruction suivante affecte une fonction lambda à la variable somme, qui calcule la somme des entiers de 1 à n :

  Exemple 1 — Lambda affectée à une variable

auto somme = [](int n) -> int {
    int s = 0;
    for (int k = 1; k <= n; k++) { s += k; }
    return s;
};

La variable somme se comporte comme une fonction : l'appel somme(7) retourne la somme des entiers naturels de 1 à 7, soit 28.

  Exemple 2 — Programme complet avec deux lambdas

#include <iostream>
using namespace std;

int main() {
    // Lambda avec type de retour explicite
    auto somme = [](int n) -> int {
        int s = 0;
        for (int k = 1; k <= n; k++) { s += k; }
        return s;
    };
    cout << "La somme de 1 a " << 7 << " = " << somme(7) << endl;

    // Type de retour omis (déduit automatiquement)
    auto fact = [](int n) {
        int f = 1;
        for (int k = 1; k <= n; k++) { f *= k; }
        return f;
    };
    cout << "La factorielle de 7 = " << fact(7);

    return 0;
}
Sortie
La somme de 1 a 7 = 28
La factorielle de 7 = 5040

Capture de variables externes

Les fonctions lambda peuvent capturer des variables du scope englobant et les utiliser lors du calcul. On liste ces variables entre les crochets [] lors de la définition de la lambda.

Modes de capture
  • Capture par valeur ([a]) : la lambda utilise la valeur que possédait la variable au moment de sa création. Les modifications ultérieures de la variable n'affectent pas la lambda.
  • Capture par référence ([&b]) : la lambda utilise la valeur actuelle de la variable au moment de son appel. Toute modification est répercutée.
  • Capture totale par valeur ([=]) : toutes les variables externes sont capturées par valeur.
  • Capture totale par référence ([&]) : toutes les variables externes sont capturées par référence.

  Exemple 3 — Capture mixte (valeur et référence)

#include <iostream>
using namespace std;

int main() {
    double a = 2.5, b = 3;

    // a capturé par valeur, b capturé par référence
    auto evaluer = [a, &b](double x) -> double {
        return a * x + b;
    };

    cout << "evaluer(2)   = " << evaluer(2)   << endl;
    cout << "evaluer(4.5) = " << evaluer(4.5) << endl;

    return 0;
}
Sortie
evaluer(2)   = 8
evaluer(4.5) = 14.25

  Exemple 4 — Impact de la modification des variables capturées

#include <iostream>
using namespace std;

int main() {
    double a = 2.5, b = 3;

    auto evaluer = [a, &b](double x) -> double {
        return a * x + b;
    };

    cout << "evaluer(2) = " << evaluer(2) << endl;  // 2.5*2+3 = 8

    a = 3;    // ignoré par la lambda (capture par valeur)
    b = 2.5;  // pris en compte (capture par référence)

    cout << "evaluer(2) = " << evaluer(2) << endl;  // 2.5*2+2.5 = 7.5

    return 0;
}
Sortie
evaluer(2) = 8
evaluer(2) = 7.5
Explication du résultat La variable a est capturée par valeur : sa nouvelle valeur 3 est ignorée, la lambda continue d'utiliser 2,5. En revanche, b est capturée par référence : après l'affectation b = 2.5, la lambda utilise cette nouvelle valeur. Le calcul devient donc 2,5 × 2 + 2,5 = 7,5.

  Exemple 5 — Capture totale par référence [&]

#include <iostream>
using namespace std;

int main() {
    double a = 2.5, b = 3;

    // Toutes les variables externes capturées par référence
    auto evaluer = [&](double x) -> double {
        return a * x + b;
    };

    cout << "evaluer(2) = " << evaluer(2) << endl;  // 2.5*2+3 = 8

    a = 3;
    b = 2.5;

    cout << "evaluer(2) = " << evaluer(2) << endl;  // 3*2+2.5 = 8.5

    return 0;
}
Sortie
evaluer(2) = 8
evaluer(2) = 8.5
Attention à la durée de vie des variables Lors d'une capture par référence, les variables référencées doivent rester en vie aussi longtemps que la lambda est susceptible d'être appelée. Une référence vers une variable locale détruite provoque un comportement indéfini.

Récapitulatif des modes de capture

SyntaxeSignificationImpact des modifications ultérieures
[x]Capture x par valeurIgnorées
[&x]Capture x par référenceRépercutées
[=]Toutes les variables par valeurIgnorées
[&]Toutes les variables par référenceRépercutées
[=, &x]Toutes par valeur, sauf x par référenceMixte

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.