IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Fonctions virtuelles et foncteurs.


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Avatar de Le Farfadet Spatial
    Homme Profil pro
    En cours de précision…
    Inscrit en
    Avril 2008
    Messages
    190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : En cours de précision…

    Informations forums :
    Inscription : Avril 2008
    Messages : 190
    Par défaut Fonctions virtuelles et foncteurs.
    Salut à tous !

    Me voici, petit nouveau sur ce forum. Pour mon premier message, j'ai une question assez technique. Laissez-moi tout d'abord vous expliquer un peu le contexte : je participe au développement d'un nouveau modèle océanographique. Il se base sur un projet plus ancien, développé par des physiciens qui ont découvert la programmation sur le tas, d'abord en Fortran puis en C. Maintenant, le code migre vers du C++. Je vous laisse imaginer le mélange... Le plus étonnant, c'est qu'il est plutôt performant, notamment en comparaison des modèles déjà existant.

    Dans certains cas, un calcul peut-être effectué de plusieurs manières, en fonction de la précision désirée. Pour pouvoir faire le choix en cours d'exécution, la procédure de calcul est envoyé sous forme de pointeur de fonction. Le problème, c'est qu'alors le polymorphisme est délicat et que les performances peuvent être médiocres, surtout en comparaison des foncteurs. Je propose donc de passer à cette dernière solution. Avant de changer le code, il m'a été demandé de faire un petit exemple et du profilage, d'abord juste avec la bibliothèque standard, ensuite avec Boost.

    Et de m'exécuter.

    Cependant, j'ai un petit problème. Soit le code suivant --- il compile, mais ne fait rien, j'essaye juste d'isoler mon problème et de montrer la hiérarchie que j'ai mise en place sans plus noyer le poisson, c'est déjà assez long comme cela :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
     
    /**
     * \file main.cpp
     * \brief Exemple d'utilisation des foncteurs en n'utilisant que la
     * bibliothèque standard.
     * \author Yoann LE BARS
     * \date 22 avril 2008
     * \version 1.0
     */
     
    #ifdef HAVE_CONFIG_H
    #include <config.h>
    #endif
     
    #include <iostream>
    #include <functional>
    #include <cmath>
    #include <cstdlib>
    #include <vector>
     
    /**
     * \namespace Fonction
     *
     * Espace de nommage contenant des foncteurs.
     */
    namespace Fonction {
      /**
       * \class FonctionSimple
       * \brief Class de base pour les fonctions d'une variable.
       */
      template <class Arg, class Res>
      class FonctionSimple: public std::unary_function<Arg, Res> {
      public:
        /**
         * \brief Surcharge de l'operateur parenthèse.
         *
         * Fonction purement virtuelle.
         */
        Res operator () (const Arg &);
      };  // class FonctionSimple
     
      /**
       * \class Normale
       * \brief Implementation sous forme de foncteur de la loi normale.
       */
      template <class T> class Normale: public FonctionSimple<T, T> {
        private:
          /// Espérance
          T mu;
          /// Écart type
          T sigma;
      public:
        /**
         * \brief Constructeur par défaut.
         * \param mu_ Initialiseur de l'espérance.
         * \param sigma_ Initialiseur de l'écart type.
         *
         * Si aucune valeur n'est donnée, initialisation à zéro.
         */
        Normale (const T &mu_ = static_cast<T>(0),
    	     const T &sigma_ = static_cast<T>(0)):
          mu (mu_), sigma (sigma_) {}  // Gaussienne (const T &, const T &)
     
        /**
         * \brief Donne l'espérance.
         * \return mu
         *
         * La modification n'est pas possible.
         */
        T esperance (void) const {return mu;}
        /**
         * \brief Donne l'écart type.
         * \return sigma
         *
         * La modification n'est pas possible.
         */
        T ecartType (void) const {return sigma;}
        /**
         * \brief Donne l'espérance.
         * \return mu
         *
         * La modification est possible.
         */
        T &esperance (void) {return mu;}
        /**
         * \brief Donne l'écart type.
         * \return sigma
         *
         * La modification est possible.
         */
        T &ecartType (void) {return sigma;}
     
        /**
         * \brief Surcharge de l'opérateur parenthèse.
         * \param x Valeur dont on veut connaître la probabilité.
         */
        T operator () (const T &x) const {
          const T facteur =
    	static_cast<T>(1) / (sigma * static_cast<T>(std::sqrt(2. * M_PI)));
          const T numerateur = x - mu;
          return facteur * std::exp(-(numerateur * numerateur)
    				/ (static_cast<T>(2) * sigma * sigma));
        }  // T operator () (const Arg &) const
      };  //  class Normale
    }  // namespace Fonction
     
    /**
     * \namespace Utile
     *
     * Espace de nommage comprenant des fonctions utilitaires.
     */
    namespace Utile {
      /**
       * \brief Tire une valeur aléatoire au sort.
       * \param min Valeur minimum.
       * \param max Valeur maximum.
       * \return Une valeur aléaatoire dans [min ; max].
       */
      template <class T> inline T aleatoire (const T &min, const T &max) throw () {
        return
          static_cast<T>(std::rand()) / static_cast<T>(RAND_MAX) * (max - min)
          + min;
      }  // template <class T> T aleatoire (const T &, const T &)
    }  // namespace Utile
     
    /**
     * \brief Corps du programme.
     * \param argc Nombre d'arguments dans la ligne de commande.
     * \param argv La ligne de commande.
     * \return 0 si tout va bien, -1 si une quelconque exception est déclanchée.
     */
    int main(int argc, char **argv) {
      try {
        std::srand(static_cast<unsigned int>(std::time(NULL)));
     
        const double mu = Utile::aleatoire(0., 100.);
        const double sigma = Utile::aleatoire(0., 100.);
        const Fonction::Normale<double> f (mu, sigma);
     
        return 0;
      }
      catch (const std::exception &e) {
        std::cerr << "Exception caught: " << e.what() << std::endl;
        return -1;
      }
    }  // main
    Par contre, si je change la définition de ma classe FonctionSimple de la sorte :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    /**
     * \class FonctionSimple
     * \brief Class de base pour les fonctions d'une variable.
     */
    template <class Arg, class Res>
    class FonctionSimple: public std::unary_function<Arg, Res> {
    public:
      /**
       * \brief Surcharge de l'operateur parenthèse.
       *
       * Fonction purement virtuelle.
       */
      virtual Res operator () (const Arg &);
    };  // class FonctionSimple
    Alors j'obtiens l'erreur suivante à l'édition des liens (et non pas à la compilation) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    Building CXX object CMakeFiles/exemplefoncteur.dir/main.o
    /usr/bin/c++   -ansi -pedantic -Wall -g -o CMakeFiles/exemplefoncteur.dir/main.o -c /home/lebars/test/exemplefoncteur/main.cpp
    Linking CXX executable exemplefoncteur
    /usr/bin/cmake -P CMakeFiles/exemplefoncteur.dir/cmake_clean_target.cmake
    /usr/bin/c++      -fPIC  -L /home/softs/fftpack/lib/ -L /home/softs/fftpack/lib/ "CMakeFiles/exemplefoncteur.dir/main.o"   -o exemplefoncteur -rdynamic
    CMakeFiles/exemplefoncteur.dir/main.o:(.rodata._ZTVN8Fonction7NormaleIdEE[vtable for Fonction::Normale<double>]+0x10): undefined reference to `Fonction::FonctionSimple<double, double>::operator()(double const&)'
    CMakeFiles/exemplefoncteur.dir/main.o:(.rodata._ZTVN8Fonction14FonctionSimpleIddEE[vtable for Fonction::FonctionSimple<double, double>]+0x10): undefined reference to `Fonction::FonctionSimple<double, double>::operator()(double const&)'
    collect2: ld returned 1 exit status
    Sachant que mon idée est d'en arriver finalement à quelque chose de ce genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    /**
     * \class FonctionSimple
     * \brief Class de base pour les fonctions d'une variable.
     */
    template <class Arg, class Res>
    class FonctionSimple: public std::unary_function<Arg, Res> {
    public:
      /**
       * \brief Surcharge de l'operateur parenthèse.
       *
       * Fonction purement virtuelle.
       */
      virtual Res operator () (const Arg &) = 0;
    };  // class FonctionSimple
    Pour pouvoir ensuite faire ce genre de choses :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    /**
     * \brief Appelle une fonction sur un vecteur de valeurs et écrit les
     * résultats sur la sortie standard.
     * \param f La fonction.
     * \param x Le vecteurs des valeurs.
     */
    template <class Arg, class Res>
    void test (const Fonction::FonctionSimple<Arg, Res> &f,
                    const std::vector<Arg> &x) {
      for (size_t i = 0; i < x.size(); ++i) std::cout << f(x[i]) << std::endl;
    }  /* template <class Arg, class Res>
            void test (const std::unary_function<Arg, Res> &,
                            const std::vector<Arg> &) */
    Je dois admettre que je ne comprends pas pourquoi cette erreur apparaît, ni surtout pourquoi elle a lieu à l'édition des liens. D'après Luc Hermitte (il me semble qu'il intervient également sur ce forum) :

    Citation Envoyé par Luc Hermitte
    Sinon, je sais qu'il y a des règles bizarres entre la virtualité et les inlines
    Ceci explique peut-être cela, si quelqu'un a une idée, je suis preneur.

    D'autre part, Luc Hermitte (toujours lui), m'a également fait cette remarque :

    Attention aux foncteurs par référence. Le fait qu'ils soient passés par valeur n'est pas innocent à mon avis. C'est une des règles conseillée dans Effective STL de Scott Meyers. En diagonale, je lis que la SL prend par valeur pour pouvoir accepter pointeur de fonction comme foncteur sans broncher. Puis toute une discussion sur le slicing qui attend au tournant et que donc pour avoir du polymorphisme, il faut introduire une indirection à la pseudo-pimpl, vu que le type d'implémentation sera celui dérivé.

    Après, je ne sais pas si cela a des impacts sur la capacité du compilo à inliner l'appel. Il faudrait faire des tests et décoder l'assembleur généré.
    Remarque pertinente, quoique j'aimerais en savoir un peu plus à ce sujet. De plus, s'agissant d'un code de calcul numérique assez intense, les performances sont tout de même relativement importantes, donc il serait bon que le développement en ligne ait lieu. Si des gens ont des précisions à ce sujet et pas seulement dans le cas particulier de GCC (j'essaye de faire en sorte que le code soit le plus portable possible), je lui en saurais gré.

    À bientôt.

    Le Farfadet Spatial

  2. #2
    Membre émérite

    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    717
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 717
    Par défaut
    Salut et bienvenu sur le forum.

    L'erreur viens tout simplement du fait que la fonction est déclarée mais nul part définie. Sans virtual, le compilateur arrive à voir que la fonction n'est jamais utilisé et du coup ne cherche même pas à la compiler. Avec virtual c'est plus difficile.

    Concernant le développement en ligne des fonctions, il faut comprendre qu'il ne peut pas avoir lieu dans un contexte de polymorphisme dynamique, c-à-d quand le choix de la fonction à appeler est déterminé à l'exécution, via une fonction virtuelle par exemple. Il n'est par contre pas incompatible avec le polymorphisme statique (template, surcharge, ...) car le choix de la fonction s'effectue au moment de la compilation.

    En général, lorsqu'on a des exigences de performances importantes comme pour du calcul numérique, on choisi le polymorphisme dynamique en dernier recours, quand les autres solutions ne conviennent pas, car il est potentiellement beaucoup plus couteux.

  3. #3
    screetch
    Invité(e)
    Par défaut
    le polymorphisme dynamique est le dispatch dynamique de code le moins couteux. il faut simplement faire la part des choses statiques et dynamiques. et lorsque c'est dynamique, c'est idiot de chercher a faire mieux qu'une methode virtuelle.

  4. #4
    Membre éclairé
    Avatar de Le Farfadet Spatial
    Homme Profil pro
    En cours de précision…
    Inscrit en
    Avril 2008
    Messages
    190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : En cours de précision…

    Informations forums :
    Inscription : Avril 2008
    Messages : 190
    Par défaut
    Salut à tous !

    Merci pour les réponses.

    Citation Envoyé par Sylvain Togni Voir le message
    L'erreur viens tout simplement du fait que la fonction est déclarée mais nul part définie. Sans virtual, le compilateur arrive à voir que la fonction n'est jamais utilisé et du coup ne cherche même pas à la compiler. Avec virtual c'est plus difficile.
    Ça explique pourquoi l'erreur survient à l'édition des liens. Par contre, si j'utilise la définition purement virtuelle suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    template <class Arg, class Res>
    class FonctionSimple: public std::unary_function<Arg, Res> {
    public:
      /**
       * \brief Surcharge de l'operateur parenthèse.
       *
       * Fonction purement virtuelle.
       */
      virtual Res operator () (const Arg &) = 0;
    };  // class FonctionSimple
    Alors j'ai toujours une erreur, à la compilation cette fois :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    /usr/bin/c++   -ansi -pedantic -Wall -g -o CMakeFiles/exemplefoncteur.dir/main.o -c /home/lebars/test/exemplefoncteur/main.cpp
    /home/lebars/test/exemplefoncteur/main.cpp: In function ‘int main(int, char**)’:
    /home/lebars/test/exemplefoncteur/main.cpp:151: error: cannot declare variable ‘f’ to be of abstract type ‘const Fonction::Normale<double>’
    /home/lebars/test/exemplefoncteur/main.cpp:45: note:   because the following virtual functions are pure within ‘const Fonction::Normale<double>’:
    /home/lebars/test/exemplefoncteur/main.cpp:38: note:    Res Fonction::FonctionSimple<Arg, Res>::operator()(const Arg&) [with Arg = double, Res = double]
    Sachant que la ligne 151 est la suivante (dans la fonction main) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
        const Fonction::Normale<double> f (mu, sigma);
    La ligne 45 :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      template <class T> class Normale: public FonctionSimple<T, T> {
    Et la ligne 38 (dans la déclaration de la classe FonctionSimple) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
        virtual Res operator () (const Arg &) = 0;
    J'espère que vous retrouvez vos petits avec ça.

    Je ne comprends pas bien : certes, FonctionSimple est une classe abstraite, mais Normale, si elle hérite de FonctionSimple, n'est pas abstraite, ce me semble.

    Concernant le développement en ligne des fonctions, il faut comprendre qu'il ne peut pas avoir lieu dans un contexte de polymorphisme dynamique, c-à-d quand le choix de la fonction à appeler est déterminé à l'exécution, via une fonction virtuelle par exemple. Il n'est par contre pas incompatible avec le polymorphisme statique (template, surcharge, ...) car le choix de la fonction s'effectue au moment de la compilation.

    En général, lorsqu'on a des exigences de performances importantes comme pour du calcul numérique, on choisi le polymorphisme dynamique en dernier recours, quand les autres solutions ne conviennent pas, car il est potentiellement beaucoup plus couteux.
    C'est vrai que vu comme ça, ma question est totalement idiote. Mais en fait, ce n'est pas tout-à-fait ce que je voulais dire (peut-être n'est-ce pas assez clair dans ma tête) : je voudrais autant que faire se peut que les choix soient fais à l'exécution. Dans ce cas, j'ai un peu de mal à avoir une idée assez générale (c'est-à-dire sans me focaliser sur la plateforme Linux + GCC) sur ce qui sera préférable en termes de performances, un passage par référence constante ou par valeur. Est-ce que le choix fait dans la bibliothèque standard est un choix de performances, de versatilité ou pour les deux ?

    À bientôt.

    Le Farfadet Spatial

  5. #5
    screetch
    Invité(e)
    Par défaut
    pour ton erreur, toute les classes derivant de FonctionSimple seront abstraite tant qu'elles n'auront pas definit une implementation de la fonction virtuelle pure operator()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template<Ret, Arg> class FonctionSimple
    {
      virtual Ret operator()(Arg arg) = 0;
    };
    virtuelle pure

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template< typename T > class Normale : FonctionSimple<T,T>
    {
      virtual T operator()(T& arg){ printf("blabla\n");
    };
    est une classe abstraite, pour devenir concrete elle doit avoir une implementation exacte
    ou Ret = T et Arg = T
    soit EXACTEMENT:
    la reference change le type et donc ca pete.
    ou alors tu peux avoir :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template< typename T > class Normale : FonctionSimple<T,T&>
    {
      virtual T operator()(T& arg){ printf("blabla\n");
    };
    et la ca marche.

    enfin, attention a la constification de la fonction operator() : elle doit etre la meme dans la classe de base (abstraite) et les classes derivées.




    Pour ton probleme de reference, je n'ai rien compris. je passe la main a un autre =)

  6. #6
    Membre éclairé
    Avatar de Le Farfadet Spatial
    Homme Profil pro
    En cours de précision…
    Inscrit en
    Avril 2008
    Messages
    190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : En cours de précision…

    Informations forums :
    Inscription : Avril 2008
    Messages : 190
    Par défaut
    Salut à tous !

    Citation Envoyé par screetch Voir le message
    enfin, attention a la constification de la fonction operator() : elle doit etre la meme dans la classe de base (abstraite) et les classes derivées.
    Une constification passagère ? Essayez les dragées Fucca !

    Bref, en fait, c'était la seule erreur : en ne changeant rien à la déclaration de la classe Normale, mais en changeant la classe fonction simple ainsi, plus de problème :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    template <class Arg, class Res>
      class FonctionSimple: public std::unary_function<Arg, Res> {
      public:
        /**
         * \brief Surcharge de l'operateur parenthèse.
         *
         * Fonction purement virtuelle.
         */
        virtual Res operator () (const Arg &) const = 0;
      };  // class FonctionSimple
    Voilà, merci déjà pour m'avoir permis de régler ce premier problème. Le code suivant correspond exactement à l'exemple que je voulais faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
     
    /**
     * \file main.cpp
     * \brief Exemple d'utilisation des foncteurs en n'utilisant que la
     * bibliothèque standard.
     * \author Yoann LE BARS
     * \date 22 avril 2008
     * \version 1.0
     */
     
    #ifdef HAVE_CONFIG_H
    #include <config.h>
    #endif
     
    #include <iostream>
    #include <functional>
    #include <cmath>
    #include <cstdlib>
    #include <vector>
     
    /**
     * \namespace Fonction
     *
     * Espace de nommage contenant des foncteurs.
     */
    namespace Fonction {
      /**
       * \class FonctionSimple
       * \brief Class de base pour les fonctions d'une variable.
       */
      template <class Arg, class Res>
      class FonctionSimple: public std::unary_function<Arg, Res> {
      public:
        /**
         * \brief Surcharge de l'operateur parenthèse.
         *
         * Fonction purement virtuelle.
         */
        virtual Res operator () (const Arg &) const = 0;
      };  // class FonctionSimple
     
      /**
       * \class Normale
       * \brief Implementation sous forme de foncteur de la loi normale.
       */
      template <class T> class Normale: public FonctionSimple<T, T> {
        private:
          /// Espérance
          T mu;
          /// Écart type
          T sigma;
      public:
        /**
         * \brief Constructeur par défaut.
         * \param mu_ Initialiseur de l'espérance.
         * \param sigma_ Initialiseur de l'écart type.
         *
         * Si aucune valeur n'est donnée, initialisation à zéro.
         */
        Normale (const T &mu_ = static_cast<T>(0),
    	     const T &sigma_ = static_cast<T>(0)):
          mu (mu_), sigma (sigma_) {}  // Gaussienne (const T &, const T &)
     
        /**
         * \brief Donne l'espérance.
         * \return mu
         *
         * La modification n'est pas possible.
         */
        T esperance (void) const {return mu;}
        /**
         * \brief Donne l'écart type.
         * \return sigma
         *
         * La modification n'est pas possible.
         */
        T ecartType (void) const {return sigma;}
        /**
         * \brief Donne l'espérance.
         * \return mu
         *
         * La modification est possible.
         */
        T &esperance (void) {return mu;}
        /**
         * \brief Donne l'écart type.
         * \return sigma
         *
         * La modification est possible.
         */
        T &ecartType (void) {return sigma;}
     
        /**
         * \brief Surcharge de l'opérateur parenthèse.
         * \param x Valeur dont on veut connaître la probabilité.
         */
        virtual T operator () (const T &x) const {
          const T facteur =
    	static_cast<T>(1) / (sigma * static_cast<T>(std::sqrt(2. * M_PI)));
          const T numerateur = x - mu;
          return facteur * std::exp(-(numerateur * numerateur)
    				/ (static_cast<T>(2) * sigma * sigma));
        }  // T operator () (const Arg &) const
      };  //  class Normale
    }  // namespace Fonction
     
    /**
     * \namespace Utile
     *
     * Espace de nommage comprenant des fonctions utilitaires.
     */
    namespace Utile {
      /**
       * \brief Tire une valeur aléatoire au sort.
       * \param min Valeur minimum.
       * \param max Valeur maximum.
       * \return Une valeur aléaatoire dans [min ; max].
       */
      template <class T> inline T aleatoire (const T &min, const T &max) throw () {
        return
          static_cast<T>(std::rand()) / static_cast<T>(RAND_MAX) * (max - min)
          + min;
      }  // template <class T> T aleatoire (const T &, const T &)
     
      /**
       * \brief Appelle une fonction sur un vecteur de valeurs et écrit les
       * résultats sur la sortie standard.
       * \param f La fonction.
       * \param x Le vecteurs des valeurs.
       */
      template <class Arg, class Res>
      void test (const Fonction::FonctionSimple<Arg, Res> &f,
    	     const std::vector<Arg> &x) {
        for (size_t i = 0; i < x.size(); ++i) std::cout << f(x[i]) << std::endl;
      }  /* template <class Arg, class Res>
    	void test (const std::unary_function<Arg, Res> &,
    		   const std::vector<Arg> &) */
    }  // namespace Utile
     
    /**
     * \brief Corps du programme.
     * \param argc Nombre d'arguments dans la ligne de commande.
     * \param argv La ligne de commande.
     * \return 0 si tout va bien, -1 si une quelconque exception est déclanchée.
     */
    int main(int argc, char **argv) {
      try {
        std::srand(static_cast<unsigned int>(std::time(NULL)));
     
        const double mu = Utile::aleatoire(0., 100.);
        const double sigma = Utile::aleatoire(0., 100.);
        const Fonction::Normale<double> f (mu, sigma);
     
        std::vector<double> x (1000);
        for (size_t i = 0; i < x.size(); ++i) x[i] = Utile::aleatoire(0., 100.);
     
        Utile::test(f, x);
     
        return 0;
      }
      catch (const std::exception &e) {
        std::cerr << "Exception caught: " << e.what() << std::endl;
        return -1;
      }
    }  // main
    Toutefois, je ne mets pas encore « résolu » dans le titre de ce sujet de discussion, car j'espère encore obtenir quelques avis au sujet du choix entre passage par valeur ou par références constantes.

    À bientôt.

    Le Farfadet Spatial

  7. #7
    Membre émérite

    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    717
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 717
    Par défaut
    Le choix entre passage par valeur ou par référence dépend de plusieurs choses :
    - Si polymorphisme dynamique alors pas le choix, seul le passage par référence fonctionnera
    - Sinon si l'objet est petit, i.e. de l'ordre de grandeur d'un type de base (int, double, pointeur, ...) et la copie peu couteuse, il y a des chances pour le passage par valeur soit plus rapide
    - Sinon passage par référence (constante)

    Pourquoi le passage par valeur peut être plus rapide ?
    1) Parce qu'il nécessite une indirection en moins
    2) Pour des raisons d'aliasing. Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void add(int tab[], int n, int const& v)
    {
        for(int i = 0; i < n; ++i)
            tab[i] += v;
    }
    Avec un passage par référence de v, le compilateur est obligé de relire la valeur de la variable v à chaque itération car elle pourrait pointer sur un élément de tab et donc changer de valeur en cour de route.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void add(int tab[], int n, int v)
    {
        for(int i = 0; i < n; ++i)
            tab[i] += v;
    }
    Alors qu'avec un passage par valeur de v, le compilateur peut mettre la valeur de v dans un registre une bonne fois pour toute en début de fonction, il sait qu'elle ne changera jamais.

    Un autre phénomène entre en compte avec les foncteurs. Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    bool sortByX(T const& t1, T const& t2) {return t1.x < t2.x;}
    std::sort(v.begin(), v.end(), sortByX);
    Ce code a des chances d'être sensiblement plus lent que le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct SortByX
    {
        bool operator()(T const& t1, T const& t2) const {return t1.x < t2.x;}
    };
    std::sort(v.begin(), v.end(), SortByX());
    Car les compilateurs arrivent plus facilement a mettre en ligne la fonction de comparaison quand elle est passé via un foncteur (car la fonction à appeler dépend du TYPE passé) que quand elle est passé via un pointeur sur fonction (car la fonction à appeler dépend de la VALEUR passée).

  8. #8
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    A partir du moment où tu as de la virtualité, tu n'as plus le choix: tu dois passer par référence.
    Si ton choix de fonction ne peut être réalisé qu'à l'exécution, tu va avoir besoin de ce polymorphisme d'inclusion dynamique. Si le choix peut être réalisé lors du codage, pourquoi ces héritages et compagnie?

    Je manque un peu de recul sur les foncteurs, mais j'ai l'impression que tu cherches à leur faire faire des choses pour lesquels ils n'ont pas été "découverts". Pour moi, un foncteur, c'est un truc qui se déguise en fonction libre. Et qui permet d'écrire des algos qui appliquent/exploitent des fonctions ... qui n'en sont pas toujours.
    Presque je me demande si avoir une hiérarchie de FonctionAToi ne suffirait pas. Dedans, tu choisi une fonction membre virtuelle exec/apply/... qui sera la truc spécialisé. L'avantage, c'est que c'est moins déroutant.

    PS: tout cela me fait penser au "cours d'algorithmie" de Stepanov (en ligne sur son site), où il définit fonctions, procédures, ....
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 05/03/2006, 19h29
  2. Compilation avec des fonctions virtuel pure
    Par vanitom dans le forum C++
    Réponses: 4
    Dernier message: 16/12/2005, 14h37
  3. masquage de fonction virtuelle
    Par julien.sagnard dans le forum C++
    Réponses: 3
    Dernier message: 27/01/2005, 14h00
  4. Réponses: 2
    Dernier message: 07/10/2004, 17h00
  5. fonctions virtuelles
    Par Mau dans le forum Autres éditeurs
    Réponses: 4
    Dernier message: 21/11/2003, 09h53

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo