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 :

class template et fonction amie


Sujet :

C++

  1. #1
    Candidat au Club
    Inscrit en
    Novembre 2010
    Messages
    11
    Détails du profil
    Informations personnelles :
    Âge : 44

    Informations forums :
    Inscription : Novembre 2010
    Messages : 11
    Points : 4
    Points
    4
    Par défaut class template et fonction amie
    Bonjour,

    J'ai du mal avec les templates. Je ne comprends pas pourquoi ce bout de code tout bête ne compile pas. J'ai beau me creuser la tête, chercher sur le web, essayer toutes les variantes que je peux imaginer... impossible de trouver la bonne syntaxe.

    Par contre, si j'enlève la déclaration d'amitié, ca marche.

    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
     
    template <class Objet>
    class Conteneur {
      public:
        // On veut ajouter le contenu des deux Conteneurs, et renvoyer le résultat par valeur
        friend const Conteneur<Objet> Add (const Conteneur<Objet> &Data1, const Conteneur<Objet> &Data2);
      protected:
        Objet *Data;
    };
     
     
    template <class Objet>
    const Conteneur<Objet> Add (const Conteneur<Objet> &Data1, const Conteneur<Objet> &Data2) {
      Conteneur<Objet> Result;
      // ... utilise les données membres protégées de Data1 et Data2 pour déterminer Result ...
      return Result;
    }
     
     
    int main(int argc, char *argv[]) {
      Conteneur<int> Data1;
      Conteneur<int> Data2;
      Conteneur<int> R = Add (Data1, Data2);
      return 0;
    }

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut, et bienvenue sur le forum.

    C'est effectivement une restriction à l'amitié qu'il est assez difficile d'expliquer, mais nous pourrions dire que c'est, simplement, parce que la classe non spécialisée (tout comme la fonction, d'ailleurs) n'a pas d'existence en soi: c'est un modèle de code qui sera utilisé par le compilateur pour créer la classe Conteneur<MachinChose> ou la fonction Add<MachinChose>.

    Ceci dit, il y a un "turn arround" assez sympa à envisager:

    La fonction add est, typiquement, un service que l'on pourrait attendre de la part d'un conteneur.

    Nous pourrions donc envisager de donner la forme suivante à Conteneur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template <class Objet>
    class Conteneur
    {
        public:
            /* tout le reste */
            Conteneur & Add(Conteneur<Objet> const & rhs)
            {
                /* calcul de la place total dont on doit disposer */
                /* ajout des éléments de rhs à "this" */
                return *this;
            }
    };
    Et la fonction libre Add pourrait alors se baser sur cette fonction membre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template <class Objet>
    Conteneur<Objet> Add(Conteneur<Objet> const & first, 
                         Conteneur<Objet> const & second)
    {
        /* faisons une copie de first */
        Conteneur<Objet> temp(first);
        /* ajoutons second à la copie */
        temp.Add(second);
        /* et renvoyons la copie qui contient maintenant le contenu des deux 
         */
        return temp;
    }
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Candidat au Club
    Inscrit en
    Novembre 2010
    Messages
    11
    Détails du profil
    Informations personnelles :
    Âge : 44

    Informations forums :
    Inscription : Novembre 2010
    Messages : 11
    Points : 4
    Points
    4
    Par défaut
    Merci beaucoup de ton accueil et de ton aide.

    Si ça n'est pas possible, je ferai sans... mais quand même, la question me tracasse.

    Mon exemple ne compile pas avec C++ Builder 6, mais je viens d'essayer avec une vieille version de DevC++ qui traînait, et il semblerait que ça fonctionne avec la syntaxe suivante (<> après le Add) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      friend const Conteneur<Objet> Add<> (const Conteneur<Objet> &Data1, const Conteneur<Objet> &Data2);
    Je me demande ce que prévoit la norme. Est ce que c++ builder n'est pas au point, ou est ce DevC++ qui a ajouté une facilité ?

    Pour le "turn arround" proposé, c'est bien comme ça que mon "vrai" code fonctionne (le code posté est épuré au max pour montrer uniquement ce qui pose problème), mais je suis surpris (et un peu déçu j'avoue...) de ne pas pouvoir autoriser la fonction Add à accéder aux données protégées du conteneur. Il me semble que dans certain cas, ça peut être nécessaire. (on pourrai aussi déclarer Conteneur& Add(Conteneur<Objet> const&, Conteneur<Objet> const&) en fonction membre, mais ça risque d'alourdir le code par la suite).

  4. #4
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par sleepless Voir le message
    Merci beaucoup de ton accueil et de ton aide.

    Si ça n'est pas possible, je ferai sans... mais quand même, la question me tracasse.

    Mon exemple ne compile pas avec C++ Builder 6, mais je viens d'essayer avec une vieille version de DevC++ qui traînait, et il semblerait que ça fonctionne avec la syntaxe suivante (<> après le Add) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      friend const Conteneur<Objet> Add<> (const Conteneur<Objet> &Data1, const Conteneur<Objet> &Data2);
    Je me demande ce que prévoit la norme. Est ce que c++ builder n'est pas au point, ou est ce DevC++ qui a ajouté une facilité ?

    Pour le "turn arround" proposé, c'est bien comme ça que mon "vrai" code fonctionne (le code posté est épuré au max pour montrer uniquement ce qui pose problème), mais je suis surpris (et un peu déçu j'avoue...) de ne pas pouvoir autoriser la fonction Add à accéder aux données protégées du conteneur. Il me semble que dans certain cas, ça peut être nécessaire. (on pourrai aussi déclarer Conteneur& Add(Conteneur<Objet> const&, Conteneur<Objet> const&) en fonction membre, mais ça risque d'alourdir le code par la suite).
    DevC++ se base sur un compilateur antédiluvien. DevC++ est lui même un IDE antédiluvien.

    Ceci dit, et vive la norme C++, une fonction ou une classe template peuvent être amie d'une classe normale. La déclaration d'amitié est syntaxiquement différente, bien que très logique :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    template <class _Type> class X { ... };
     
    class Y
    {
      // X<> est une classe amie
      template <class _Type> friend X;
    };
    A noter qu'on peut par la suite spécialiser X (partiellement, si nécessaire). Ca va marcher quand même.

    Si la classe qui contient la déclaration d'amitié est elle aussi une classe template, et si l'argument template est le même, on retombe sur le classique:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    template <class _Type> class X { ... };
     
    template <class _Type> class Y
    {
      friend class X;
    }
    Si on souhaite qu'une fonction template soit amie d'une classe, on utilise le même principe :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    template <class _Type>
    _Type operation(const param_t& value)
    { ... }
     
    class Y
    {
      template <class _Type> friend _Type operation<_Type>(const param_t&);
    };
    Si on souhaite attaque une version spécialisée de la fonction, rien de plus facile (la spécialisation doit avoir été déclarée avant la clause friend):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class Y
    {
      template <> friend MonType operation<MonType>(const param_t&);
    };
    Enfin, si on veut être ami d'une fonction template lorsqu'on est une classe template :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    template <class _Type> class Y
    {
      friend _Type operation<_Type>(const param_t&);
    };
    Voilà. Je n'ai pas de compilateur pour tester, donc il est possible qu'il reste une ou deux pitite erreur. Mais dans l'idée, c'est ça.

    Dans ton cas spécifique, ton code devrait être le suivant :

    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
     
    template <class Objet>
    class Conteneur {
      public:
        // On veut ajouter le contenu des deux Conteneurs, 
        // et renvoyer le résultat par valeur
        // XXX : la modification est là: c'est proche de ce
        // que DevC++ accepte. 
        friend const Conteneur<Objet> Add<Objet> (
           const Conteneur<Objet> &Data1, 
           const Conteneur<Objet> &Data2);
     
      protected:
        Objet *Data;
    };
     
     
    template <class Objet>
    const Conteneur<Objet> Add (
       const Conteneur<Objet> &Data1, 
       const Conteneur<Objet> &Data2) 
    {
      Conteneur<Objet> Result;
      // ... utilise les données membres protégées de 
      // Data1 et Data2 pour déterminer Result ...
      return Result;
    }
     
    int main(int argc, char *argv[]) {
      Conteneur<int> Data1;
      Conteneur<int> Data2;
      Conteneur<int> R = Add (Data1, Data2);
      return 0;
    }
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  5. #5
    Candidat au Club
    Inscrit en
    Novembre 2010
    Messages
    11
    Détails du profil
    Informations personnelles :
    Âge : 44

    Informations forums :
    Inscription : Novembre 2010
    Messages : 11
    Points : 4
    Points
    4
    Par défaut
    Le code que tu proposes marche parfaitement sous DevC++, mais toujours pas sous builder 6. Merci 1000 fois quand même, car tu m'as mis sur la voie :

    La fonction amie doit avoir été déclaré avant avant la clause friend !

    Et dans mon cas, ca implique une déclaration forward de la class Conteneur. Ainsi, le code suivant compile enfin sous builder 6


    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
     
     
    // Déclaration forward
    template <class Objet>
    class Conteneur;
     
    // Déclaration de la fonction amie, elle doit etre faite AVANT la déclaration d'amitié !
    // (ici on la définie, mais on pourrait seulement la déclarer)
    // Par contre, on a besoin que la classe Conteneur soit déjà déclarée
    template <class Objet>
    const Conteneur<Objet> Add (const Conteneur<Objet> &Data1, const Conteneur<Objet> &Data2) {
      Conteneur<Objet> Result;
      // ... utilise les données protégées de Data1 et Data2 pour déterminer Result ...
      Result.Data = NULL; // juste pour tester si on y a bien acces
      return Result;
    }
     
     
    template <class Objet>
    class Conteneur {
      public:
        // On veut ajouter le contenu des deux Conteneurs, et renvoyer le résultat par valeur
         friend const Conteneur<Objet> Add<> (const Conteneur<Objet> &Data1, const Conteneur<Objet> &Data2);
      protected:
        Objet *Data;
    };
     
    int main(int argc, char *argv[]) {
      Conteneur<int> Data1;
      Conteneur<int> Data2;
      Conteneur<int> R = Add (Data1, Data2);
      return 0;
    }
    }

  6. #6
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par sleepless Voir le message
    Le code que tu proposes marche parfaitement sous DevC++, mais toujours pas sous builder 6. Merci 1000 fois quand même, car tu m'as mis sur la voie :

    La fonction amie doit avoir été déclaré avant avant la clause friend !

    Et dans mon cas, ca implique une déclaration forward de la class Conteneur. Ainsi, le code suivant compile enfin sous builder 6


    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
     
     
    // Déclaration forward
    template <class Objet>
    class Conteneur;
     
    // Déclaration de la fonction amie, elle doit etre faite AVANT la déclaration d'amitié !
    // (ici on la définie, mais on pourrait seulement la déclarer)
    // Par contre, on a besoin que la classe Conteneur soit déjà déclarée
    template <class Objet>
    const Conteneur<Objet> Add (const Conteneur<Objet> &Data1, const Conteneur<Objet> &Data2) {
      Conteneur<Objet> Result;
      // ... utilise les données protégées de Data1 et Data2 pour déterminer Result ...
      Result.Data = NULL; // juste pour tester si on y a bien acces
      return Result;
    }
     
     
    template <class Objet>
    class Conteneur {
      public:
        // On veut ajouter le contenu des deux Conteneurs, et renvoyer le résultat par valeur
         friend const Conteneur<Objet> Add<> (const Conteneur<Objet> &Data1, const Conteneur<Objet> &Data2);
      protected:
        Objet *Data;
    };
     
    int main(int argc, char *argv[]) {
      Conteneur<int> Data1;
      Conteneur<int> Data2;
      Conteneur<int> R = Add (Data1, Data2);
      return 0;
    }
    }
    C'est vrai, je ne l'ai pas dit : une déclaration d'amitié ne permet pas de déclarer une fonction template. Cette déclaration doit exister avant.

    Ma coulpe.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

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

Discussions similaires

  1. Template et fonction amie / type de retour inconnu
    Par Armas dans le forum Débuter
    Réponses: 13
    Dernier message: 24/04/2013, 15h17
  2. Réponses: 32
    Dernier message: 28/11/2010, 17h33
  3. Classe Template et fonction virtuelle cachée
    Par benbarate dans le forum C++
    Réponses: 5
    Dernier message: 14/05/2010, 09h09
  4. [C++] Fonction amie vituelle dans une classe ?
    Par Bob.Killer dans le forum C++
    Réponses: 11
    Dernier message: 04/12/2005, 13h42
  5. Problèmes de fonctions membres de classe templates, gcc3.3.6
    Par yves.dessertine dans le forum Autres éditeurs
    Réponses: 12
    Dernier message: 17/10/2005, 21h36

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