Résumé – Chap 8 – Les expressions lambda
Une expression lambda permet de définir directement une opération à l’endroit où elle doit être utilisée, sans devoir créer séparément une fonction classique. Elle est particulièrement adaptée aux algorithmes de la bibliothèque standard auxquels il faut fournir un comportement, par exemple une condition de recherche, une transformation ou une règle de comparaison. Sa forme générale comporte une liste de captures, des paramètres et un corps. Une lambda crée en réalité un objet appelable, appelé objet de fermeture, dont le type est généré par le compilateur ; auto est donc particulièrement pratique pour le manipuler. Les paramètres fonctionnent comme ceux d’une fonction ordinaire et le type de retour peut généralement être déduit automatiquement, mais il peut également être indiqué explicitement. Pour utiliser des variables locales extérieures, la lambda doit les capturer. Une capture par valeur crée une copie de la variable au moment de la création de la lambda, alors qu’une capture par référence donne accès à la variable originale et permet d’observer ses modifications ultérieures. Plusieurs variables peuvent être capturées de différentes manières, et les formes [=] et [&] permettent respectivement une capture par défaut par valeur ou par référence. Le mot-clé mutable autorise la modification, à l’intérieur de la lambda, des copies obtenues par capture par valeur sans modifier les variables originales. Une capture par référence impose en revanche de rester attentif à la durée de vie de l’objet référencé lorsqu’une lambda est conservée et utilisée ultérieurement.
Les lambdas prennent toute leur importance lorsqu’elles sont associées aux algorithmes génériques. std::find_if peut recevoir une lambda exprimant la condition que doit satisfaire l’élément recherché, std::for_each peut appliquer une opération à tous les éléments d’un intervalle, std::sort peut recevoir une règle de comparaison personnalisée et std::transform peut produire de nouvelles valeurs à partir d’une plage existante. Une lambda permet ainsi de placer le comportement exactement à proximité de l’algorithme qui l’utilise, ce qui évite de multiplier les petites fonctions auxiliaires lorsque celles-ci n’ont d’intérêt qu’à un seul endroit. Elle peut également servir à trier des objets selon l’un de leurs attributs ou à exprimer des traitements dépendant de variables disponibles dans le contexte de l’appel. Les lambdas génériques étendent encore cette possibilité en utilisant auto dans leurs paramètres, ce qui permet au compilateur de générer un opérateur d’appel adapté aux types effectivement utilisés. Une fonction classique reste préférable lorsqu’un traitement possède un nom significatif, doit être réutilisé à plusieurs endroits ou mérite une existence indépendante ; la lambda est particulièrement naturelle pour un comportement local et étroitement lié à une opération. Elle constitue ainsi un élément central du C++ moderne en reliant de manière concise les données, les algorithmes et les opérations spécifiques que le programme souhaite leur appliquer.