Pourquoi l'expression lambda?
Considérez l'énoncé suivant :
int monInt = 52 ;Ici, myInt est un identifiant, une lvalue. 52 est un littéral, une prvalue. Aujourd'hui, il est possible de coder spécialement une fonction et de la mettre en position 52. Une telle fonction est appelée une expression lambda. Considérez également le programme court suivant :
#inclureen utilisant l'espace de noms std ;
int fn(int par)
réponse int = par + 3 ;
retourner la réponse ;
int main()
nf(5) ;
renvoie 0 ;
Aujourd'hui, il est possible de coder spécialement une fonction et de la mettre à la position de l'argument de 5, de l'appel de fonction, fn(5). Une telle fonction est appelée une expression lambda. L'expression lambda (fonction) dans cette position est une valeur pr.
Tout littéral à l'exception du littéral de chaîne est une valeur pr. L'expression lambda est une conception de fonction spéciale qui s'adapterait comme un littéral dans le code. C'est une fonction anonyme (sans nom). Cet article explique la nouvelle expression primaire C++, appelée expression lambda. Des connaissances de base en C++ sont indispensables pour comprendre cet article.
Contenu de l'article
- Illustration de l'expression lambda
- Parties de l'expression lambda
- Captures
- Schéma de fonction de rappel classique avec expression Lambda
- Le type à retour suiveur
- Fermeture
- Conclusion
Illustration de l'expression lambda
Dans le programme suivant, une fonction, qui est une expression lambda, est affectée à une variable :
#inclureen utilisant l'espace de noms std ;
fn auto = [](int param)
réponse int = param + 3;
retourner la réponse ;
;
int main()
variable automatique = fn(2) ;
cout << variab << '\n';
renvoie 0 ;
La sortie est :
5En dehors de la fonction main(), il y a la variable, fn. Son type est automatique. Auto dans cette situation signifie que le type réel, tel que int ou float, est déterminé par l'opérande droit de l'opérateur d'affectation (=). A droite de l'opérateur d'affectation se trouve une expression lambda. Une expression lambda est une fonction sans le type de retour précédent. Notez l'utilisation et la position des crochets, []. La fonction renvoie 5, un int, qui déterminera le type de fn.
Dans la fonction main(), il y a l'instruction :
variable automatique = fn(2) ;Cela signifie que fn en dehors de main(), finit comme l'identifiant d'une fonction. Ses paramètres implicites sont ceux de l'expression lambda. Le type de variab est auto.
Notez que l'expression lambda se termine par un point-virgule, tout comme la définition de classe ou de structure, se termine par un point-virgule.
Dans le programme suivant, une fonction, qui est une expression lambda renvoyant la valeur 5, est un argument d'une autre fonction :
#inclureen utilisant l'espace de noms std ;
void otherfn (int no1, int (*ptr)(int))
int no2 = (*ptr)(2);
cout << no1 << " << no2 << '\n';
int main()
otherfn(4, [](int param)
réponse int = param + 3;
retourner la réponse ;
);
renvoie 0 ;
La sortie est :
4 5Il y a deux fonctions ici, l'expression lambda et la fonction otherfn(). L'expression lambda est le deuxième argument de otherfn(), appelé dans main(). Notez que la fonction lambda (expression) ne se termine pas par un point-virgule dans cet appel car, ici, c'est un argument (pas une fonction autonome).
Le paramètre de fonction lambda dans la définition de la fonction otherfn() est un pointeur vers une fonction. Le pointeur porte le nom, ptr. Le nom, ptr, est utilisé dans la définition otherfn() pour appeler la fonction lambda.
La déclaration,
int no2 = (*ptr)(2);Dans la définition otherfn(), il appelle la fonction lambda avec un argument de 2. La valeur de retour de l'appel, "(*ptr)(2)" de la fonction lambda, est affectée à no2.
Le programme ci-dessus montre également comment la fonction lambda peut être utilisée dans le schéma de fonction de rappel C++.
Parties de l'expression lambda
Les parties d'une fonction lambda typique sont les suivantes :
[] ()- [] est la clause de capture. Il peut avoir des articles.
- () est pour la liste des paramètres.
- est pour le corps de la fonction. Si la fonction est autonome, elle doit se terminer par un point-virgule.
Captures
La définition de la fonction lambda peut être affectée à une variable ou utilisée comme argument d'un autre appel de fonction. La définition d'un tel appel de fonction doit avoir comme paramètre, un pointeur vers une fonction, correspondant à la définition de la fonction lambda.
La définition de la fonction lambda est différente de la définition de la fonction normale. Il peut être affecté à une variable dans la portée globale ; cette fonction-assignée-à-variable peut aussi être codée à l'intérieur d'une autre fonction. Lorsqu'il est affecté à une variable de portée globale, son corps peut voir d'autres variables dans la portée globale. Lorsqu'il est affecté à une variable dans une définition de fonction normale, son corps ne peut voir d'autres variables dans la portée de la fonction qu'avec l'aide de la clause de capture, [].
La clause de capture [], également connue sous le nom d'introducteur lambda, permet d'envoyer des variables de la portée (fonction) environnante dans le corps de la fonction de l'expression lambda. Le corps de la fonction de l'expression lambda est censé capturer la variable lorsqu'il reçoit l'objet. Sans la clause capture [], une variable ne peut pas être envoyée de la portée environnante dans le corps de la fonction de l'expression lambda. Le programme suivant illustre cela, avec la portée de la fonction main(), comme portée environnante :
#inclureen utilisant l'espace de noms std ;
int main()
int id = 5;
fn automatique = [id]()
cout << id << '\n';
;
fn();
renvoie 0 ;
La sortie est 5. Sans le nom, id, à l'intérieur de [], l'expression lambda n'aurait pas vu l'identifiant de la variable de la portée de la fonction main().
Capture par référence
L'exemple ci-dessus d'utilisation de la clause capture capture par valeur (voir les détails ci-dessous). Dans la capture par référence, l'emplacement (stockage) de la variable, e.g., id ci-dessus, de la portée environnante, est rendu disponible dans le corps de la fonction lambda. Ainsi, changer la valeur de la variable à l'intérieur du corps de la fonction lambda changera la valeur de cette même variable dans la portée environnante. Chaque variable répétée dans la clause de capture est précédée de l'esperluette (&) pour atteindre cet objectif. Le programme suivant illustre cela :
#inclureen utilisant l'espace de noms std ;
int main()
int id = 5; pied flottant = 2.3 ; car ch = 'A';
fn automatique = [&id, &ft, &ch]()
identifiant = 6 ; pi = 3.4 ; ch = 'B';
;
fn();
cout << id << ", " << ft << ", " << ch << '\n';
renvoie 0 ;
La sortie est :
6, 3.4, BConfirmation que les noms de variables à l'intérieur du corps de fonction de l'expression lambda correspondent aux mêmes variables en dehors de l'expression lambda.
Capture par valeur
Lors de la capture par valeur, une copie de l'emplacement de la variable, de la portée environnante, est rendue disponible à l'intérieur du corps de la fonction lambda. Bien que la variable à l'intérieur du corps de la fonction lambda soit une copie, sa valeur ne peut pas être modifiée à l'intérieur du corps pour le moment. Pour réaliser la capture par valeur, chaque variable répétée dans la clause capture n'est précédée de rien. Le programme suivant illustre cela :
#inclureen utilisant l'espace de noms std ;
int main()
int id = 5; pied flottant = 2.3 ; car ch = 'A';
fn automatique = [id, ft, ch]()
//id = 6; pi = 3.4 ; ch = 'B';
cout << id << ", " << ft << ", " << ch << '\n';
;
fn();
identifiant = 6 ; pi = 3.4 ; ch = 'B';
cout << id << ", " << ft << ", " << ch << '\n';
renvoie 0 ;
La sortie est :
5, 2.3, un6, 3.4, B
Si l'indicateur de commentaire est supprimé, le programme ne compilera pas. Le compilateur émettra un message d'erreur indiquant que les variables à l'intérieur de la définition du corps de la fonction de l'expression lambda ne peuvent pas être modifiées. Bien que les variables ne puissent pas être modifiées à l'intérieur de la fonction lambda, elles peuvent être modifiées en dehors de la fonction lambda, comme le montre la sortie du programme ci-dessus.
Mélange de captures
La capture par référence et la capture par valeur peuvent être mélangées, comme le montre le programme suivant :
#inclureen utilisant l'espace de noms std ;
int main()
int id = 5; pied flottant = 2.3 ; car ch = 'A'; bool bl = vrai;
fn automatique = [id, ft, &ch, &bl]()
ch = 'B'; bl = faux;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
renvoie 0 ;
La sortie est :
5, 2.3, B, 0Lorsque tous capturés, sont par référence :
Si toutes les variables à capturer sont capturées par référence, alors un seul & suffira dans la clause de capture. Le programme suivant illustre cela :
#inclureen utilisant l'espace de noms std ;
int main()
int id = 5; pied flottant = 2.3 ; car ch = 'A'; bool bl = vrai;
fn automatique = [&]()
identifiant = 6 ; pi = 3.4 ; ch = 'B'; bl = faux;
;
fn();
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
renvoie 0 ;
La sortie est :
6, 3.4, B, 0Si certaines variables doivent être capturées par référence et d'autres par valeur, alors un & représentera toutes les références, et les autres ne seront chacune précédées de rien, comme le montre le programme suivant :
en utilisant l'espace de noms std ;int main()
int id = 5; pied flottant = 2.3 ; car ch = 'A'; bool bl = vrai;
fn automatique = [&, id, ft]()
ch = 'B'; bl = faux;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
renvoie 0 ;
La sortie est :
5, 2.3, B, 0Notez que & seul (je.e., & non suivi d'un identifiant) doit être le premier caractère de la clause de capture.
Lorsque tous capturés, sont par valeur :
Si toutes les variables à capturer doivent être capturées par valeur, alors un seul = suffira dans la clause de capture. Le programme suivant illustre cela :
#inclureen utilisant l'espace de noms std ;
int main()
int id = 5; pied flottant = 2.3 ; car ch = 'A'; bool bl = vrai;
fn automatique = [=]()
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
renvoie 0 ;
La sortie est :
5, 2.3, A, 1Noter: = est en lecture seule, à partir de maintenant.
Si certaines variables doivent être capturées par valeur et d'autres par référence, alors un = représentera toutes les variables copiées en lecture seule, et les autres auront chacune &, comme le montre le programme suivant :
#inclureen utilisant l'espace de noms std ;
int main()
int id = 5; pied flottant = 2.3 ; car ch = 'A'; bool bl = vrai;
fn automatique = [=, &ch, &bl]()
ch = 'B'; bl = faux;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn();
renvoie 0 ;
La sortie est :
5, 2.3, B, 0Notez que = seul doit être le premier caractère de la clause de capture.
Schéma de fonction de rappel classique avec expression Lambda
Le programme suivant montre comment un schéma de fonction de rappel classique peut être réalisé avec l'expression lambda :
#inclureen utilisant l'espace de noms std ;
char *sortie;
auto cba = [](char out[])
sortie = sortie ;
;
void principalFunc(char input[], void (*pt)(char[]))
(*pt)(entrée);
cout<<"for principal function"<<'\n';
vide fn()
cout<<"Now"<<'\n';
int main()
char input[] = "pour la fonction de rappel" ;
principalFunc(entrée, cba);
fn();
cout<