Expressions lambda en C++
Dans Visual C++, une expression lambda, ou lambda, est une sorte de fonction anonyme dont l'état reste inchangé et qui peut accéder aux variables disponibles dans la portée englobante. Pour cela, elle définit une classe et crée un objet de ce type. Cet article explique ce que sont les expressions lambda, les compare à d'autres techniques de programmation, décrit leurs avantages et fournit un exemple simple.
À propos des expressions lambda
De nombreux langages de programmation prennent en charge le concept de fonction anonyme, qui possède un corps, mais pas de nom. Une expression lambda est une technique de programmation qui est liée aux fonctions anonymes. Une expression lambda définit implicitement une classe d'objets de fonction et construit un objet de fonction de ce type de classe. Pour plus d'informations sur les objets de fonction, consultez Objets de fonction.
En guise d'introduction, la norme ISO C++ montre une expression lambda dans le contexte d'un paramètre passé à la fonction std::sort() :
#include <algorithm>
#include <cmath>
void abssort(float* x, unsigned n) {
std::sort(x, x + n,
// Lambda expression begins
[](float a, float b) {
return (std::abs(a) < std::abs(b));
} // end of lambda expression
);
}
Cet article explique le fonctionnement de cette expression.
Important
Les expressions lambda ne sont pas prises en charge dans les entités managées suivantes du Common Langage Runtime (CLR) : ref class, ref struct, value class et value struct.
Objets de fonction comparés aux expressionsLambdas
Lorsque vous écrivez du code, vous utilisez probablement des pointeurs de fonction et des objets de fonction pour résoudre des problèmes et effectuer des calculs, surtout lorsque vous utilisez des algorithmes STL. Les pointeurs de fonction et les objets de fonction présentent des avantages et des inconvénients. Par exemple, les pointeurs de fonction possèdent une charge mémoire syntaxique minimale, mais leur état n'est pas conservé au sein d'une portée. Les objets de fonction, quant à eux, peuvent conserver leur état, mais ils nécessitent la charge mémoire syntaxique d'une définition de classe.
Une expression lambda combine les avantages des pointeurs et des objets de fonction, tout en évitant leurs inconvénients. Tout comme les objets de fonction, les expressions lambda sont flexibles et peuvent conserver leur état. Toutefois, contrairement aux objets de fonction, leur syntaxe compacte ne requiert pas de définition de classe. Grâce aux expressions lambda, votre code est moins encombré et moins sujet aux erreurs que le code nécessaire pour un objet de fonction équivalent.
Les exemples suivants comparent l'utilisation d'une expression lambda et d'un objet de fonction. Le premier exemple utilise une expression lambda pour afficher sur la console si chaque élément d'un objet vector est pair ou impair. Le deuxième exemple utilise un objet de fonction pour accomplir la même tâche.
Exemple 1 : Utilisation d'une expression lambda
Cet exemple utilise une expression lambda incorporée dans l'appel de fonction for_each pour afficher sur la console si chaque élément d'un objet vector est pair ou impair.
Code
// even_lambda.cpp
// compile with: cl /EHsc /nologo /W4 /MTd
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// Create a vector object that contains 10 elements.
vector<int> v;
for (int i = 1; i < 10; ++i) {
v.push_back(i);
}
// Count the number of even numbers in the vector by
// using the for_each function and a lambda.
int evenCount = 0;
for_each(v.begin(), v.end(), [&evenCount] (int n) {
cout << n;
if (n % 2 == 0) {
cout << " is even " << endl;
++evenCount;
} else {
cout << " is odd " << endl;
}
});
// Print the count of even numbers to the console.
cout << "There are " << evenCount
<< " even numbers in the vector." << endl;
}
Sortie
Commentaires
Dans l'exemple, le troisième argument de la fonction for_each est une expression lambda. La partie [&evenCount] spécifie la clause de capture de l'expression, (int n) spécifie la liste des paramètres, et la partie restante spécifie le corps de l'expression.
Exemple 2 : Utilisation d'un objet de fonction
Il arrive qu'une expression lambda soit trop complexe pour être utilisée autrement que dans l'exemple précédent. L'exemple suivant utilise un objet de fonction au lieu d'une expression lambda, en même temps que la fonction for_each, pour produire les mêmes résultats que dans l'exemple 1. Les deux exemples indiquent le nombre de chiffres pairs dans un objet vector. Pour conserver l'état de l'opération, la classe FunctorClass enregistre la variable m_evenCount par référence comme variable membre. Pour exécuter l'opération, FunctorClass implémente l'opérateur d'appel de fonction, operator(). Le compilateur Visual C++ génère un code qui est comparable en taille et en performances au code de l'expression lambda de l'exemple 1. Pour un problème simple comme celui de cet article, la plus simple des expressions lambda convient probablement mieux qu'un objet de fonction. Toutefois, si vous pensez que les fonctionnalités peuvent nécessiter une expansion significative à l'avenir, il serait plus judicieux d'utiliser un objet de fonction afin que la maintenance du code soit facilitée.
Pour plus d'informations sur le operator(), consultez Appel de fonction (C++). Pour plus d'informations sur la fonction for_each, consultez for_each.
Code
// even_functor.cpp
// compile with: /EHsc
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
class FunctorClass
{
public:
// The required constructor for this example.
explicit FunctorClass(int& evenCount)
: m_evenCount(evenCount) { }
// The function-call operator prints whether the number is
// even or odd. If the number is even, this method updates
// the counter.
void operator()(int n) const {
cout << n;
if (n % 2 == 0) {
cout << " is even " << endl;
++m_evenCount;
} else {
cout << " is odd " << endl;
}
}
private:
// Default assignment operator to silence warning C4512.
FunctorClass& operator=(const FunctorClass&);
int& m_evenCount; // the number of even variables in the vector.
};
int main()
{
// Create a vector object that contains 10 elements.
vector<int> v;
for (int i = 1; i < 10; ++i) {
v.push_back(i);
}
// Count the number of even numbers in the vector by
// using the for_each function and a function object.
int evenCount = 0;
for_each(v.begin(), v.end(), FunctorClass(evenCount));
// Print the count of even numbers to the console.
cout << "There are " << evenCount
<< " even numbers in the vector." << endl;
}
Sortie
Résumé
Les expressions lambda sont une technique de programmation puissante et expressive. Pour en savoir plus sur les composantes et les propriétés d'une expression lambda, consultez Syntaxe d'expression lambda. Pour savoir comment utiliser les expressions lambda dans vos programmes, consultez Exemples d'expressions lambda.