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 :

syntaxe portable pour les opérateurs amis de classes templates


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 38
    Points : 39
    Points
    39
    Par défaut syntaxe portable pour les opérateurs amis de classes templates
    Bonjour,

    le problème me semble si classique que j'imagine qu'il a déjà été rencontré de nombreuses fois, pourtant je n'en ai trouvé aucune réponse satisfaisante, ni ici (faq et posts précédents) ni ailleurs sur le web.

    Je souhaite définir un patron de classe dont l'essentiel est présenté ici :

    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
    template<typename T>
    class foo {
     
     public:
      typedef T value_type;
      typedef T *iterator;
      typedef const T *const_iterator;
      typedef T &reference;
      typedef const T &const_reference;
      typedef size_t size_type;
     
      typedef foo self;
     
      template <typename Iter_T>
      void copy(Iter_T first, Iter_T last);
     
      iterator begin();
      iterator end();
      const_iterator begin() const;
      const_iterator end() const;
      reference operator[](size_type n);
      const_reference operator[](size_type n) const;
      static size_type size();
     
      // friend operators
      friend self operator+ (self x, const self &y);
      friend self operator-  (self x, const self &y);
      friend self operator+ (self x, value_type y);
      friend self operator- (self x, value_type y);
      friend self operator* (self x, value_type y);
      friend self operator/ (self x, value_type y);
      friend self operator% (self x, value_type y);
    };
    Les définitions des fonctions et opérateurs membres et amis doivent être placées à l'extérieur pour des raisons de lisibilité de l'interface.

    A priori, la déclaration de classe précédente est anodine et sans surprise (cf. par exemple le C++ Cookbook de chez O'Reilly qui propose une classe kvector très similaire), l'ennui c'est qu'elle ne passe la compilation, en tout cas sous g++ 3.4.3 et icc 9.0 (de Intel) disponible sur ma station de travail (Linux). Une recherche sur le web me porte à croire que la version actuelle du Comeau (considéré comme très conforme au standard) ne l'accepte pas non plus.

    Tous exigent des chevrons <> après le nom des opérateurs (au passage, ces chevrons ne sont pas spécifiés dans l'exemple C.13.2 du Stroustrup sur les opérateurs amis de classes templates)

    Avec g++ 3.4.3 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ./foo.h:51: warning: friend declaration `foo<T, N> operator+(foo<T, N>, const foo<T, N>&)' declares a non-template function
    ./foo.h:51: warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) -Wno-non-template-friend disables this warning
    Avec icc 9.0 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    warning #1624: "foo<T, N>::self operator+(foo<T, N>::self, const foo<T, N>::self &)" declares a non-template function -- add <> to refer to a template instance
        friend self operator+ (self x, const self &y);
    Qu'a cela ne tienne, rajoutons les chevrons demandés :
    friend self operator+ <>(self x, const self &y);

    Voici maintenant les réactions des compilateurs :
    Avec g++ 3.2.3 :
    cela compile

    Avec g++ 3.4.3 et supérieur (au moins jusqu'à la version 4.0) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ./foo.h:51: error: declaration of `operator+' as non-function
    ./foo.h:51: error: expected `;' before '<' token
    Avec icc 9.0 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    ./foo.h(51): error: operator+ is not a template
        friend self operator+ <>(self x, const self &y);
                    ^
              detected during instantiation of class "foo<Value_T, N> [with Value_T=int, N=4U]" at line 9 of "./test_foo.cpp"
    Ma connaissance du standard C++ actuel étant très fragmentaire, les messages précédents me plongent dans un abime de perplexité. De plus, j'ai du mal à imaginer des bogues de compilateur, sur 2 compilateurs différents et plusieurs versions du même compilateur (inutile de poster pour suggérer cela, sauf preuves ou commentaires sérieux à l'appui).

    Je recherche si possible une solution portable à ce problème.

    Merci d'avance de vos interventions.

  2. #2
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Points : 15 920
    Points
    15 920
    Par défaut
    Je crois me souvenir qu'il faut une déclaration de tes opérateurs amis avant la définition de ta classe (et donc une déclaration anticipée de ta classe juste avant). A vérifier.

  3. #3
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Je crois bien que ce problème est traité dans la FAQ C++ Lite (du moins la version originale)

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    258
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France, Bas Rhin (Alsace)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 258
    Points : 307
    Points
    307
    Par défaut
    La declaration des amis doit effectivement etre presente avant l'expression friend :
    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
     
    template<typename T>
    class foo;
     
    template<typename T>
    foo<T> operator+(foo<T> const &, foo<T> const &);
     
    template<typename T>
    class foo
    {
        friend foo<T> operator+<T>(foo<T> const &, foo<T> const &);
    };
     
    template<typename T>
    foo<T> operator+(foo<T> const & f1, foo<T> const & f2)
    {
        return foo<T>();
    }
    Rien a voir avec le probleme original, mais quelle est ta raison pour declararer un operateur + et pas d'operateur += ?

  5. #5
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Points : 15 920
    Points
    15 920
    Par défaut
    La declaration des amis doit effectivement etre presente avant l'expression friend
    En fait j'avais dit une bêtise : dans ce cas là la déclaration anticipée de la classe ne suffira pas, les opérateurs ayant un paramètre et leur retour par valeur.

    Au pire la solution qui marche à tous les coups, c'est de te faire l'ami du template d'opérateur (ie. ami de toutes les instanciations).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template <class T>
    class foo
    {
       ...
     
       template <class U> friend foo<U> operator +(foo<U>, const foo<U>&);
    };

  6. #6
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 38
    Points : 39
    Points
    39
    Par défaut Merci à tous
    Bonjour

    La solution de Laurent, qui consiste à passer par un paramètre template U supplémentaire (tout bête mais je n'y avais pas pensé), m'a l'air de fonctionner même s'il ne la juge pas idéale (elle passe au moins l'étape de compilation sur mes versions courantes de g++ et icc). Je pense que je vais l'adopter à défaut de mieux (mais est-il possible de faire mieux compte tenu du standard, je n'en suis pas sûr).

    Je vais attendre un peu en cas de nouvelles réactions ou propositions puis je noterai ce topic comme résolu.

    Loufoque, tu évoques une solution dans la faq. Personnellement je n'avais rien trouvé mais j'ai peut-être mal cherché. Peux-tu préciser ton lien ou au moins le sujet ?

    Roulious, pour répondre à ta question sur l'opérateur +=, en fait il est bien présent mais pour alléger le code j'avais supprimé de l'extrait tous les opérateurs membres (dont l'opérateur +=), pour ne garder que les amis.

    Encore merci à tous

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    258
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France, Bas Rhin (Alsace)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 258
    Points : 307
    Points
    307
    Par défaut
    Citation Envoyé par Corentor
    Roulious, pour répondre à ta question sur l'opérateur +=, en fait il est bien présent mais pour alléger le code j'avais supprimé de l'extrait tous les opérateurs membres (dont l'opérateur +=), pour ne garder que les amis.
    Si tu as un operateur += et un operateur +, tu n'as en general pas besoin de jouer avec l'amitie.

    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
     
    template<typename T>
    class MaClasse
    {
    public :
        MaClasse<T> & operator+=(MaClasse<T> const & rhs);
    };
     
    template<typename T>
    MaClasse<T> operator+(MaClasse<T> const & lhs, MaClasse<T> const & rhs)
    {
        MaClasse<T> resultat(lhs);
        resultat += rhs;
        return resultat;
    }
    En utilisant ce type de code, tu n'as pas besoin de dupliquer le code des operateurs (appel a += depuis +), ni de declarer des amis (l'operateur += est public).

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 38
    Points : 39
    Points
    39
    Par défaut
    Bonne remarque Roulious.

    En fait, mes opérateurs | étaient déjà implémentés en fonction de des |=. Mon utilisation de l'amitié était stupide dans ce cas particulier, j'en conviens (va falloir que je me couche plus tôt le soir moi). Ce qui ne réduit en rien l'intérêt de la discussion dans le cas où l'amitié s'impose vraiment.

  9. #9
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Dans l'espoir d'expliquer ce qui se passe, regardons un exemple regroupant tous les cas de fonctions amies d'une classe template.

    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
     
    template <typename T> struct Foo;
     
    template <typename T> void friendFn2(Foo<T> const&);
    template <typename T> void friendFn3(Foo<T> const&);
    template <typename T> void friendFn4(Foo<T> const&);
    template <typename T> void friendFn5(Foo<int> const&, T const&);
     
    template <typename T> struct Foo
    {
      friend void friendFn1(Foo const&);          // 1
      friend void friendFn2<T>(Foo const&);       // 2
      friend void friendFn3<>(Foo const&);        // 3
      friend void friendFn4(Foo const&);          // 4
      template <typename U> friend void friendFn5(Foo const&, U const&); // 5
    private:
      int x;
    };
     
    void friendFn1(Foo<int> const& f) { f.x; }  // 6
    template<typename T> void friendFn2(Foo<T> const& f) { f.x; } // 7
    template<typename T> void friendFn3(Foo<T> const& f) { f.x; } // 8
    template<typename T> void friendFn4(Foo<T> const& f) { f.x; } // 9
    void friendFn4(Foo<int> const& f) { f.x; } // 10
    template<typename T> void friendFn5(Foo<int> const& f, T const&) { f.x; } // 11
     
    int main()
    {
      Foo<int> f;        
      friendFn1(f);    // 12
      friendFn2(f);    // 13
      friendFn3(f);    // 14
      friendFn4<>(f);  // 15
      friendFn4(f);    // 16
      friendFn5(f, 1); // 17
    }
    La ligne 1 déclare comme amie une fonction non template prenant donc une instantiation de la classe comme paramètre. Cette fonction est définie à la ligne 6 et appelée à la ligne 12.

    La ligne 2 déclare comme amie une la spécialisation d'une fonction template. La fonction est définie à la ligne 7 et appelée à la ligne 13.

    La ligne 3 est une syntaxe alternative qui peut être utilisée quand tous les paramètres templates peuvent être déduit des arguments.

    La ligne 5 déclare comme amies toutes les spécialisation d'une fonction template. La fonction est définie à la ligne 11 et appelée ligne 17.

    La subtilité intervient avec la ligne 4. Le C++ permet de surcharger une fonction template avec une fonction non template. Dans ce cas il peut y avoir donc une spécialisation de la fonction template qui a le même prototype que la fonction non template. Dans ce cas, la fonction non template est préférée à la fonction template, mais on peut spécifier qu'il faut appeler la fonction template avec la syntaxe de la ligne 15.

    La ligne 4 est de même structure que la ligne 1, donc elle déclare comme amie la fonction non template définie à la ligne 10 et appelée ligne 16.

    La fonction template de même nom, définie à la ligne 9 et appelée ligne 15 n'est pas amie, donc le programme comme tel ne doit pas compiler tant qu'elle tente d'accéder à un membre privé ou qu'on ne l'a pas déclarée amie avec la syntaxe de la ligne 2 ou de la ligne 3.

    Comme il est rare qu'on désigne déclarer comme amie des fonctions non templates et que la subtilité ci-dessus fait des dégats, certains compilateurs donnent un avertissement. C'est ce que faisaient g++ et icc.

    Tu as ajouté les <>, mais les syntaxes des lignes 2 et 3 ne sont valables que si on a déjà déclaré les fonctions avant. D'où l'erreur que tu as eue.

  10. #10
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par Laurent Gomila
    En fait j'avais dit une bêtise : dans ce cas là la déclaration anticipée de la classe ne suffira pas, les opérateurs ayant un paramètre et leur retour par valeur.
    La définition de la classe n'est nécessaire que pour la définition de la fonction ou l'utilisation de celle-ci. Tu peux déclarer une fonction ayant des paramètres par valeur et retournant un type incomplet. Il n'y a aucun problème avec:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template <typename T> struct Foo;
    template <typename T> Foo<T> operator+(Foo<T> const&, Foo<T> const&);
    template <typename T> struct Foo 
    {
    ...
       friend Foo operator+<>(Foo const&, Foo const&);
    };

  11. #11
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 38
    Points : 39
    Points
    39
    Par défaut
    Merci Jean Marc pour ces explications très claires. Ton exemple répond parfaitement au problème posé.

    J'ignorais en particulier la possibilité de surcharger une fonction template par une fonction non template. Je n'ai personnellement pas l'intention d'utiliser cette fonctionnalité (bien que je conçois qu'il puisse exister de rares cas où elle serait utile), mais il est bon d'en être informé ne serait-ce que pour s'en prémunir.

    Vos interventions ont fini par me mettre la puce à l'oreille : les fonctions n'étaient pas correctement pré-déclarées contrairement à ce que je croyais. J'avais bien déclaré en avant la classe et les fonctions, mais avec une erreur de signature passée inaperçue ce qui correspondait donc à la déclaration d'une fonction différente, d'où l'erreur.

    Un grand merci

  12. #12
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    [QUOTE=Corentor]J'ignorais en particulier la possibilité de surcharger une fonction template par une fonction non template.[quote]

    Que celui qui n'ignore rien en ce qui concerne les templates et la surcharge lève le doigt. Je ne lève pas le mien.

    Je n'ai personnellement pas l'intention d'utiliser cette fonctionnalité (bien que je conçois qu'il puisse exister de rares cas où elle serait utile), mais il est bon d'en être informé ne serait-ce que pour s'en prémunir.
    Il n'est pas possible de spécialiser les fonctions templates. Un des work around est de les surcharger.

  13. #13
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    258
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France, Bas Rhin (Alsace)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 258
    Points : 307
    Points
    307
    Par défaut
    Citation Envoyé par Jean-Marc.Bourguet
    Il n'est pas possible de spécialiser les fonctions templates. Un des work around est de les surcharger.
    C'est possible de specialiser des fonctions templates, a condition que la specialisation soit complete :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    template<typename T>
    void f()
    {
        // Generique
    }
     
    template<>
    void f<int>()
    {
        // Specialisation complete pour int
    }
    La specialisation /partielle/ est en revanche interdite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template<typename T>
    void f<T*>()
    {
        // Specialisation partielle pour tous les pointeurs
    }
    ne devrait pas compiler

  14. #14
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par roulious
    C'est possible de specialiser des fonctions templates
    Exact, j'ai été imprécis.

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

Discussions similaires

  1. Syntaxe apropriée pour les constructeurs
    Par alves1993 dans le forum Débuter
    Réponses: 2
    Dernier message: 25/07/2013, 23h16
  2. Réponses: 4
    Dernier message: 16/01/2007, 15h27
  3. [Sécurité] Sécurité portable pour les données utilisées dans une requête
    Par berceker united dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 21/07/2006, 14h48
  4. [Conseil]Portable pour les études
    Par Mayhem dans le forum Ordinateurs
    Réponses: 11
    Dernier message: 22/08/2005, 17h04

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