Le passage d’une fonction (ou plutôt d’un pointeur de fonction) en paramètre d’une autre fonction est une technique très ancienne, puisqu’elle date de l’apparition du langage C.
Il est indispensable de la connaître pour cette raison (c’est la seule possibilité de “généricité” de traitement en C), et elle est encore utilisée par certains langages comme Ada95.
Cependant, le C++ offre un moyen beaucoup plus élégant, et surtout plus performant : les functors (voir l’amphi correspondant).
Au lieu de passer une fonction f1()
en paramètre d’une autre fonction f2()
, il suffit de créer une classe contenant cette fonction f1()
comme méthode (fonction-membre, opération), d’instancier cette classe en un objet obj
, de passer cet objet à la fonction f2()
, qui appellera la méthode f1()
de son paramètre : param.f1 (...)
.
class Obj { // ... public : ... f1 (...) { ... } // f1 // ... }; // Obj void f2 (Obj & param) { // ... ... param.f1 (...); // ... } // F2()
Le dernier obstacle est l’obligation qu’a la fonction f2()
de connaître l’identificateur de la fonction f1()
.
Il est levé en surchargeant l’opérateur ()
dans la classe et en donnant à cet opérateur le même profil et le même corps que la fonction f1()
.
Un objet de la classe ainsi modifiée est appelé un “objet-fonction” ou plus simplement un functor
Ainsi, la fonction f2()
se contente d’appeler param (...)
au lieu de param.f1 (...)
.
Travail à effectuer
Créer le projet Functor
.
remplacer le contenu du fichier main.cpp
par celui du fichier TestFunctor.cpp
/** * * @file TestFunctor.cpp * * @authors M. Laporte, D. Mathieu * * @date 27/04/2010 * * @version V1.0 * * @brief Premier functor * **/ #include <iostream> #include <cassert> #include <cctype> // tolower(), toupper() #include <string> using namespace std; namespace { class ToLower { // ToDo }; // ToLower typedef int (* fctInt2Int_t) (int); string & moulinette (string & str, fctInt2Int_t transf) { for (string::size_type i (str.size ()); i--; ) str [i] = transf (str [i]); return str; } // moulinette() void testFunctor (void) { cout << "Functor : "; string ligne ("AZECv qrgWSg wrV wfdgWFDHG wdfGWXCV"); string minusc ("azecv qrgwsg wrv wfdgwfdhg wdfgwxcv"); // ToDo assert (minusc == moulinette (ligne, ...)); cout << "OK\n"; } // testFunctor() } // namespace anonyme int main (void) { /* */ testFunctor (); /* */ return 0; } // main()
Complétez la classe ToLower
possédant :
- un destructeur virtuel,
- une surcharge (virtuelle) de l’opérateur
()
, ayant le même profil que la fonction Ctolower()
:int tolower (int);
qui se contente d’appeler cette dernière et d’en renvoyer le résultat.
Modifiez la fonction moulinette()
en remplaçant le second paramètre par un functor de la classe ToLower
, et complétez l’appel de la fonction assert()
dans la fonction testFunctor()
.
Remarque : la lourdeur de mise en œuvre est à peu près du même ordre que celle de l’utilisation des pointeurs de fonction, et, pour le moment, la fonction moulinette()
ne peut faire qu’une seule opération : mettre en
minuscules.
Nous verrons ultérieurement les avantages décisifs des functors.