M2103-TP8-Exo-3

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 un Pers, second paramètre de l’opérateur () de TriParAgeAsc).

Très peu de modifications sont nécessaires :

  • ajoutez le fichier inclus functional dans lequel est déclaré le helper bind2nd(),
  • 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';