Remarque préliminaire : cet exercice ne peut être fait qu’après les exercices précédents sur les functors.
Outre les fonctions-objets unaires, la bibliothèque standard C++ utilise des fonctions-objets binaires, ou binary functors, qui possèdent un paramètre supplémentaire, dont le type est lui aussi générique.
En revanche, elle n’utilise aucune fonction-objet à plus de deux paramètres.
C’est ce que nous aurions pu utiliser dans l’exercice Functor-comparateur abstrait générique qui n’est pas autre chose qu’un prédicat à deux paramètres de même type.
Nous allons donc le généraliser dans cet exercice.
D’autre part, nous avons montré que, selon les fonctions standard utilisées, il est nécessaire de développer pour une même classe et pour un même critère de comparaison à la fois un functor unaire et un functor binaire.
Par exemple, pour la relation d’ordre “age croissant” de la classe Pers
, un functor binaire TriParAgeAsc
est nécessaire à la fonction standard générique sort()
, et un functor unaire SelParTrancheAge
est nécessaire à la fonction standard générique find_if()
.
Nous allons montrer comment un functor binaire peut être simplement transformé en functor unaire sans réécriture.
Travail à effectuer
Créez le projet Adaptor
.
Tri
Dans votr main.cpp
copier le fichier de test :
/** * * @file FunctorSort.cpp * * @authors M. Laporte, D. Mathieu * * @date 07/12/2011 * * @version V1.0 * **/ #include <string> #include <vector> #include <algorithm> // sort() #include <iostream> using namespace std; namespace { template <typename T> class ILessThanGen { public : virtual ~ILessThanGen (void) {} virtual bool operator () (const T &, const T &) const = 0; }; // ILessThanGen class Pers { string myNom; unsigned myAge; public : Pers (const string & Nom, unsigned Age) : myNom (Nom), myAge (Age) {} const string & getNom (void) const noexcept { return myNom; } unsigned getAge (void) const noexcept { return myAge; } private : ostream & display (ostream & os) const { return os << getAge () << " - " << getNom (); } // display() public : friend ostream & operator << (ostream & os, const Pers & p) { return p.display (os); } }; // Pers class TriParAgeAsc : public ILessThanGen <Pers> { public : virtual ~TriParAgeAsc (void) noexcept {} virtual bool operator () (const Pers & p1, const Pers & p2) const noexcept { return p1.getAge () < p2.getAge (); } // operator () }; // TriParAgeAsc class TriParNomDesc : public ILessThanGen <Pers> { public : virtual ~TriParNomDesc (void) noexcept {} virtual bool operator () (const Pers & p1, const Pers & p2) const noexcept { return p1.getNom () > p2.getNom (); } // operator () }; // TriParNomDesc void functorSort (void) { cout << "FunctorSort : \n"; typedef vector <Pers> CVPers; CVPers vPers; vPers.push_back ( Pers ("Charlotte", 21)); vPers.push_back ( Pers ("Alfred", 12)); vPers.push_back ( Pers ("Jean", 42)); vPers.push_back ( Pers ("Noemie", 11)); vPers.push_back ( Pers ("Berthe", 99)); vPers.push_back ( Pers ("Agathe", 29)); vPers.push_back ( Pers ("Sylvain", 42)); vPers.push_back ( Pers ("Pierre", 75)); cout << "\nTri par age croissant\n\n"; sort (vPers.begin (), vPers.end (), TriParAgeAsc ()); for (const Pers & personne : vPers) cout << personne << '\n'; cout << "\nTri par nom decroissant\n\n"; sort (vPers.begin (), vPers.end (), TriParNomDesc ()); for (const Pers & personne : vPers) cout << personne << '\n'; } // functorSort() } // namespace int main (void) { functorSort (); return 0; } // main()
, qui est le corrigé de l’exercice Functors et algorithmes de tri.
Remplacez la classe ILessThanGen
par la classe IBinaryFunction
à trois paramètres de généricité (les types des deux paramètres et de la valeur de retour), selon le modèle suivant :
template <typename T1, typename T2, typename TRes> class IBinaryFunction { public : typedef T1 first_argument_type; typedef T2 second_argument_type; typedef TRes result_type; ...
Attention : les identificateurs des types sont imposés par bind2nd()
,
Modifiez en conséquence les classes TriParAgeAsc
et TriParNomDesc
.
Le profil d’une fonction de tri comme par exemple :
template <typename Iter_t> void selectSort (Iter_t deb, Iter_t fin, const LessThan & IsInf);
devrait être modifié en :
template <typename Iter_t, typename T> void selectSort (Iter_t deb, Iter_t fin, const IBinaryFunction <T, T, bool> & isInf); </t,>
En revanche, l’utilisation de la fonction sort()
ne nécessite aucune modification.
Compilez et testez.
Recherche
Les functors binaires que nous avons écrits devraient pouvoir être utilisés pour faire des recherches.
Par exemple, le prédicat TriParAgeAsc
renvoie true
lorsque l’âge du premier paramètre est inférieur ou égal à celui du second paramètre.
Nous souhaitons donc l’utiliser pour rechercher le premier élément dont l’âge est <=
à un âge donné, par exemple 40
, quel que soit le nom de ce dernier.
Il suffit donc de comparer successivement tous les éléments du conteneur à la constante Pers ("", 40)
.
Malheureusement, la fonction find_if()
déjà étudiée précédemment, nécessite un functor unaire.
La bibliothèque fournit la fonction bind2nd
(appelée un helper), qui renvoie un functor unaire à partir :
- de son premier paramètre qui doit être un functor binaire (nous utiliserons ici une instance de la classe
TriParAgeAsc
), - de son second paramètre qui doit être un objet de même type que le second paramètre de l’opérateur
()
du functor binaire (ici unPers
, second paramètre de l’opérateur()
deTriParAgeAsc
).
Très peu de modifications sont nécessaires :
- ajoutez le fichier inclus
functional
dans lequel est déclaré le helperbind2nd()
, - ajoutez en fin de la fonction
Adaptor()
(functorSort ()) le code suivant à compléter :cout << "\nRecherche de la premiere personne d'age <= 40 : "; CVPers::const_iterator pos = find_if (vPers.begin (), vPers.end (), // a completer if (vPers.end () == pos) cout << "Aucun element correspondant\n"; else cout << *pos << '\n'; cout << "\nRecherche de la premiere personne d'age<= 4 : "; pos = find_if (vPers.begin (), vPers.end (), // a completer if (vPers.end () == pos) cout << "Aucun element correspondant\n"; else cout << *pos << '\n';