Partage
  • Partager sur Facebook
  • Partager sur Twitter

spécialisation fonction membre template

comment spécialiser une fonction membre d'un patron de classe ?

Sujet résolu
    25 août 2018 à 12:57:58

    Bonjour à tous. Je suis en train d'apprendre le C++, et je découvre le merveilleux monde des templates.

    Cependant, je tombe sur un os. Je n'arrive pas à spécialiser des fonctions membres de patrons de classe pour certains

    paramètres de type. Les exemples de mon cours ne fonctionne pas non plus.

    voila le code :

    #include <iostream>
    
    using namespace std;
    
    template <class T> class point
    {
        T x; T y;
      public:
        point (T abs=0; T ord=0) { x = abs; y = ord; }
        void affiche () const;
    };
    
    template <class T> void point<T>::affiche () const
    { cout << "Coordonnees : " << x << " " << y << "\n"; 
    }
    
    void point<char>::affiche () const
    { cout << "Coordonnees : " << int(x) << " " << int(y) << "\n";
    }
    

    A ce moment là CODE::Blocks m'envois ce message d'erreur :

    error : specializing member 'point<char>::affiche' requires 'template<>' syntax

    Du coup j'essaye le code ci-dessous : 

    // au lieu de :
     void point<char>::affiche () const
    
    // je met :
     template <class T> void point<char>::affiche () const

    et j'obtient ces messages d'erreurs :

    error: prototype for 'void point<char>::affiche() const' does not match any in class 'point<char>'

    error: candidate is: void point<T>::affiche() const [with T = char]

    Et ce que ça veut dire qu'il faut que je déclare la fonction scpécialisé dans la déclaration de la classe ?

    Si oui, comment ?

    J'ai essayé :

    //...
    void affiche () const;
    //essais :
    point<char> void affiche () const;
    void point<char> affiche () const;
    template <class T> point<char> void affiche () const;
    
    

    aucun n'a marché. La troisième m'a envoyé de nombreux messages d'erreurs.

    AIDEZ MOI S'IL VOUS PLAÎT !!!!!



    • Partager sur Facebook
    • Partager sur Twitter
      25 août 2018 à 16:10:47

      Salut,

      Déjà, ce n'est pas Code::Blocks qui t'envoie l'erreur (lui, il se contente d'afficher l'erreur envoyée par le compilateur), mais bon...  Ce n'est qu'un détail ;)

      Quant à ton problème, il est simple : si tu veut fournir une spécialisation pour une fonction membre d'une classe template, tu dois explicitement indiquer qu'il s'agit d'une classe template, même si tous les paramètres template ont été fournis.  Cela donnera donc quelque chose ressemblant à

      template <>
      void point<char>::affiche () const
      { cout << "Coordonnees : " << int(x) << " " << int(y) << "\n";
      }

      D'ailleurs, c'est ce que te dit ton compilateur:

      error : specializing member 'point::affiche' requires 'template<>' syntax

      se traduisant (littéralement) en

      erreur: spécialiser le mobre 'point::affiche' requière la syntaxe 'template<>'

      Quelques remarques au passage:

      - PAS de using namespace std; dans un fichier d'en-tête (idéalement, il ne faudrait jamais l'utiliser)

      - Si tu n'implémente pas une fonction de ta classe template directement dans la déclaration de ta classe, tu devrais la déclarer comme inline, parce qu'elle ne sera pas implicitement considérée comme telle et que, du coup, chaque unité de compilation l'utilisant en contiendra le code binaire exécutable, ce qui énervera l'éditeur de liens

      - Une ligne, une instruction : T x; T y; devrait donc se faire sur deux lignes

      - Eviter le transtypage "barbare à la C" et préférer les transtypage nommés comme static_cast (dans le cas présent

      -Pourquoi utiliser une fonction très spécifique afficher au lieu d'utiliser l'opérateur << qui peut être adapté à n'importe quel flux de sortie ?

      • Partager sur Facebook
      • Partager sur Twitter
      Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
        25 août 2018 à 16:16:34

        Salut,

        il y a plusieurs mauvaises pratique là dedans.

        Déjà using namespace std.

        Ensuite, c'est ridicule de définir un type pour des coordonnées numériques. Autant implémenter directement ta classe avec int ou double comme type des coordonnées ;)

        Puis, ceci est à bannir :

        foo(T bar = value); // chez toi, à la ligne 9

        Tu ne sais pas à l'avance quel type l'utilisateur pourrait envoyer à ce template, donc il ne faut pas l'initialiser avant, au risque de se retrouver avec quelque chose comme ça :

        struct MyStrcut {
            ...
        };
        
        foo(MyStruct bar = 0);

        Ce qui, bien sûr, ne fonctionne pas.

        Après, ceci n'a pas de sens avec ce que tu souhaites faire :

        template <class T> class point;

        Ton compilateur s'attend à recevoir un objet en paramètre de template, mais là tu lui donnes le type char.

        Pour préciser que l'on attend un type, et pas un objet, on utilise typename :

        template <typename T> class point;

        Et enfin, tu as fais une erreur de frappe :

        point (T abs = 0; T ord = 0);

        Remplace par :

        point (T abs = 0, T ord = 0);

        Cela devrait fonctionner à présent.

        PS: grillé :)

        -
        Edité par vanaur 25 août 2018 à 16:18:10

        • Partager sur Facebook
        • Partager sur Twitter

        Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...

          25 août 2018 à 16:38:42

          Au fait: donner une valeur par défaut pour les deux paramètres dans le constructeur n'est pas logique, car cela permet d'utiliser la syntaxe

          • Point{} (utilise les deux valeurs par défaut)
          • Point{x} (utilise la valeur de x pour abs, la valeur par défaut pour ord)
          • Point{x, y} (utilise la valeur de x pour abs et la valeur de y pour ord)

          Or, sur les trois possibilités, seules la première et la dernière ont réellement du sens et seront effectivement utilisées: soit on crée notre point avec les valeurs par défaut (abs = 0 et ord = 0), soit crée un point en fournissant  son abscisse ET son ordonnée.

          Et, il se fait que l'on va tout faire pour respecter la règle qui consiste à

          rendre l'interface de vos classes faciles à utiliser correctement et difficiles à utiliser de manière incorrecte

          Malheureusement pour nous, permettre à l'utilisateur de créer un point en ne donnant que la valeur de son abscisse représente bel et bien une possibilité d'utiliser notre classe de manière incorrecte.

          Du coup, bien que cela soit plus embêtant, il faut veiller à ne fournir que les deux constructeurs qui nous intéressent réellement sous une forme qui serait du coup proche de

          template <typename T>
          class Point{
          public:
              Point(): Point{0,0}{} // (*)
              Point(T abs, T ord):abs{abs},ord{ord}{ // (**)
              }
              inline void affiche() const;
          private: // (***)
              T abs;
              T ord;
          };

          (*) 1- Depuis 2011, il est possible de déléguer l'appel d'un constructeur à un autre

          2- Depuis 2011 toujours, il est possible d'utiliser une syntaxe "normalisée" utilisant les accolade lorsque l'on veut définir la valeur d'une variable lors de sa création

          (**) reprend tout ce qui est dit en (*) et utilise la liste d'initialisation, qui devrait être préférée (depuis toujours) pour le constructeur (le corps du constructeur ne contenant que ce qui ne peut pas être fait dans la liste d'initialisation).

          Car la liste d'initialisation fonctionnera toujours alors que l'affectation dans le corps du constructeur peut -- dans certaines circonstances -- être purement et simplment impossible

          (***) Par habitude, on préférera exposer les parties publiques en premier, suivies par les parties protégées et, enfin, par les parties privées.

          Cela permet à l'utilisateur d'avoir les informations qui l'intéresse (les parties publique) en premier, et donc d'avoir "plus d'informations utiles" en haut du fichier d'en-tête, sans devoir aller chercher "dans les profondeurs" du fichiers.

          Bien sur, il y aura toujours "quelques cas" dans lesquels nous ne pourrons pas nous y tenir, mais c'est malgré tout une habitude "relativement saine" à prendre ;)

          -
          Edité par koala01 25 août 2018 à 16:44:56

          • Partager sur Facebook
          • Partager sur Twitter
          Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
            25 août 2018 à 20:30:25

            Tout d'abord, merci à tous pour vos réponses très éclairantes et vos conseils.

            Ensuite, je souhaiterais poser quelques questions supplémentaires et éclairer certains de mes choix.

            Koala01

            Vanaur

            -Tout ce code n'a en effet aucun sens, car ce n'est qu'un exercice pour apprendre à utiliser les templates.

             (d'où les fonctions et les classes inutiles)

            -Pourquoi "using namespace std" est à proscrire ?

            Koala01 : 

            -Qu'entends tu exactement par déléguer l'appel d'un constructeur à un autre ? Serait-ce similaire à la sur-définition de fonction ?

            -le fait d'empêcher l'utilisation du constructeur en ne mettant pas de liste d'initialisation n'est il pas un bon moyen de 

            "rendre l'interface de vos classes faciles à utiliser correctement et difficiles à utiliser de manière incorrecte"  

            -faut il déclarer toutes les fonctions membres d'une classe template "inline" ? Même un fonction de 100 lignes par exemple ?

            Vanaur : 

            -que signifie "foo" ? C'est un terme usuel pour désigner un constructeur ?

            -
            Edité par clementboutaric 25 août 2018 à 20:30:50

            • Partager sur Facebook
            • Partager sur Twitter
              25 août 2018 à 22:26:21

              clementboutaric a écrit:

              Pourquoi "using namespace std" est à proscrire ?

              Pour tout ça.

              clementboutaric a écrit:

              Que signifie "foo" ? C'est un terme usuel pour désigner un constructeur ?

              "foo" est simplement un mot du jargon informatique qui désigne tout et rien, on l'utilise pour faire une généralité afin de ne pas être embrouillé avec d'autres choses ;

              T foo;
              if (foo == bar);
              T foo();
              #include <foo>
              ...

              Tout comme "T", qui désigne souvent un type.

              • Partager sur Facebook
              • Partager sur Twitter

              Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...

                26 août 2018 à 0:48:38

                clementboutaric a écrit:

                -Qu'entends tu exactement par déléguer l'appel d'un constructeur à un autre ? Serait-ce similaire à la sur-définition de fonction ?

                C'est la fameuse ligne
                Point(): Point{0,0}{} // (*)

                Point() déclare (et implémente) un constructeur de la classe Point qui ne nécessite aucun paramètre.  Les deux points : introduisent la liste d'initialisation et Point{0,0} fait appel (délèguent la création du point) au constructeur de la classe qui nécessite deux paramètres pour fonctionner. 

                Enfin la paire d'accolades qui suit (celle qui est vide qui est vide) indique le "corps de la fonction", qui n'a ... plus rien à faire, vu que tout a été "délégué" au constructeur qui a besoin de deux paramètres ;)

                Cette manière de faire n'est apparue qu'en C++11, parce que, avant cela, nous ne pouvions faire appel qu'aux constructeurs des données membres (ou au constructeur de la classe de base, dans le cadre d'un héritage) de la classe dans la liste d'initialisation. 

                • Partager sur Facebook
                • Partager sur Twitter
                Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                  26 août 2018 à 0:51:28

                  D'accord, je vois. Merci encore.
                  • Partager sur Facebook
                  • Partager sur Twitter

                  spécialisation fonction membre template

                  × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                  × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                  • Editeur
                  • Markdown