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

Langage C++ Discussion :

[Undefined reference] Polymorphisme avec de l'héritage de classe template


Sujet :

Langage C++

  1. #1
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut [Undefined reference] Polymorphisme avec de l'héritage de classe template
    Bonsoir.

    Je bug sur une undefined reference. J'ai une classe template grid_graph qui contient une méthode template. J'ai une autre classe (non template) héritant de grid_graph, et je voudrais surcharger cette méthode template mais lors de la compilation il me lance un beau "undefined reference to". La méthode est bien implémenté dans le cpp...

    Merci de votre aide.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,
    Citation Envoyé par Trademark Voir le message
    Bonsoir.

    Je bug sur une undefined reference. J'ai une classe template grid_graph qui contient une méthode template. J'ai une autre classe (non template) héritant de grid_graph, et je voudrais surcharger cette méthode template mais lors de la compilation il me lance un beau "undefined reference to". La méthode est bien implémenté dans le cpp...

    Merci de votre aide.
    C'est, justement, là l'erreur...

    A moins d'avoir recours à l'instanciation explicite, les fonctions (membres de classes) templates doivent etre définies dans le fichier d'en-tête...

    En effet, le compilateur ne saura générer le code binaire qu'une fois qu'il disposera d'un type pour l'ensemble des paramètres template attendus, ce qui revient à dire qu'il ne pourra le faire que... lors de l'utilisation réelle de la classe ou de la fonction

    Pour qu'il puisse arriver à générer le code binaire, il faut, en outre, qu'il dispose du code C++ de la fonction

    Or, si tu mets l'implémentation de la fonction dans un fichier *.cpp, comme tu n'incluras jamais un *.cpp dans n'importe quel fichier, le compilateur ne trouvera jamais le code au départ duquel il doit créer le code binaire

    Résultat des courses: le compilateur considère que la fonction existe (vu qu'il en a trouvé la déclaration) mais l'éditeur de liens ne trouve pas le symbole correspondant dans le binaire, et tu as une erreur de type "undefined reference"
    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
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    F.A.Q. : Pourquoi mes templates ne sont-ils pas reconnus à l'édition des liens ?

    Citation Envoyé par Trademark Voir le message
    je voudrais surcharger cette méthode template
    Les fonctions génériques ne pouvant être virtuelles, dans ta classe dérivée tu masques ta fonction dans ta classe de base. A priori, cela ne me semble pas super comme truc.

  4. #4
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    Bonjour et merci beaucoup pour votre aide. J'avais déjà posté une erreur similaire mais c'était à cause d'une classe template. Je ne savais pas que toutes les méthodes templates devaient également être dans le .hpp.

    fonctions génériques ne pouvant être virtuelles, dans ta classe dérivée tu masques ta fonction dans ta classe de base. A priori, cela ne me semble pas super comme truc.
    Je vais essayer de mieux expliquer ce "design".
    J'ai une classe template grid_graph, elle représente un graphe en 2 dimensions et possède la propriété intéressante qu'il n'est pas nécessaire de stocker les arcs (car ils sont implicites). Le template de cette classe est le type de la valeur qu'on souhaite stocker sur les sommets.

    C'est donc une classe générique, elle offre une fonction qui permet d'insérer dans un container les voisins d'un noeud donné :

    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
    template <typename value_type>
    template <typename neighbor_container>
    void grid_graph<value_type>::neighbor(vertex_type &vertex, neighbor_container &neighbors)
    {
      for(int d=0; d < 4; ++d)
        try{
          neighbors.push_back(next(vertex, direction(d)));
        }catch(std::out_of_range &oor){}
    }
     
    template <typename value_type>
    typename grid_graph<value_type>::vertex_type grid_graph<value_type>::next(vertex_type &vertex, direction x)
    {
     
      static const std::size_t x_shift[4] = {0, 1, 0, -1};
      static const std::size_t y_shift[4] = {-1, 0, 1, 0};
     
      std::size_t new_x = vertex.first + x_shift[x];
      std::size_t new_y = vertex.second + y_shift[x];
     
      if(new_x >= x_max || new_y >= y_max)
        throw std::out_of_range("");
      return std::make_pair<std::size_t, std::size_t>(new_x, new_y);
    }
    La classe dérivé s'appelle maze. Je vous donne l'entête :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct maze_vertex
    {
      char walls;
      int altitude;
    };
     
    class maze : public grid_graph<maze_vertex>
    {
    //...
    };
    Mais il faut donc surcharger les deux méthodes (et d'autres) précédemment citées, car il ne faut plus renvoyer tous les voisins mais seulement ceux qui ne sont pas séparés par un mur. La méthode neighbor reste la même mais la méthode next change. Un des problèmes, c'est qu'il a quand même fallu ré-implémenter neighbor à l'identique de la classe mère, pour qu'il se serve de la bonne fonction next...

    Si vous avez des suggestions d'améliorations, n'hésitez pas

    Merci

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,

    Citation Envoyé par Trademark Voir le message
    Si vous avez des suggestions d'améliorations, n'hésitez pas
    Va jusqu'au bout du CRTP :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <typename value_type>
    template <typename neighbor_container>
    void grid_graph<value_type>::neighbor(vertex_type &vertex, neighbor_container &neighbors)
    {
      for(int d=0; d < 4; ++d)
        try{
          neighbors.push_back(static_cast<value_type*>(this)->next(vertex, direction(d)));
        }catch(std::out_of_range &oor){}
    }

  6. #6
    Membre Expert Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Par défaut
    EDIT : Après relecture du lien, j'ai bien compris, on "sauvegarde" bien le type en le passant en paramètre template à la classe. Merci du conseil.

    Il s'agit d'un cast d'un objet de type grid_graph<maze_vertex>*, soit this, en maze_vertex*, qui est la donnée, et qui par conséquence ne contient pas de méthode next.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    error: invalid static_cast from type ‘grid_graph<maze_vertex>* const’ to type ‘maze_vertex*’
    En fait, mais je ne sais pas si c'est possible, il faudrait redescendre dans la classe fille. C'est-à-dire que :

    (1) Regarde si neighbor est implémenté dans maze.
    (2) Non, donc va voir dans la classe mère grid_graph<maze_vertex>.
    (3) Le trouve donc lance l'exécution de la méthode.
    (4) Quand l'instruction next arrive, retourne dans la classe fille.

    En fait il faudrait pouvoir "sauvegarder" le type de la classe fille pour pouvoir faire le cast sur this ? Enfin ça me semble alambiqué.

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    J'avais lu trop rapidement ton code et je ne m'étais pas aperçu que ce n'était pas un CRTP que tu avais mis en œuvre.

    Ce que tu veux faire (quoique assez tordu donc un peu suspect) est fort possible :
    1/ Tu utilises bien le CRTP :
    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
    // avec un regroupement
    template<class params>
    class grid_graph
    {
    //....
    };
     
    template<class value_t, class type_t>
    struct grid_graph_trait
    {
       typedef type_t type;
       typedef value_t value;
    };
     
    struct maze_vertex
    {
    //...
    };
     
    class maze : public grid_graph<grid_graph_trait<maze_vertex, maze> >
    {
    //...
    };
    2/ Tu utilises l'introspection pour savoir si la classe dérivée contient la fonction ou non.

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

Discussions similaires

  1. Double héritage de classe template, quelques questions
    Par the_angel dans le forum Langage
    Réponses: 2
    Dernier message: 29/07/2012, 12h26
  2. Réponses: 8
    Dernier message: 20/03/2011, 02h21
  3. " undefined reference to " avec methode template
    Par coldrink dans le forum Langage
    Réponses: 3
    Dernier message: 24/10/2009, 20h05
  4. Héritage de classes templates (CGAL)
    Par gilouu dans le forum C++
    Réponses: 5
    Dernier message: 08/10/2009, 11h30
  5. Héritage de classe template
    Par FunkyTech dans le forum Langage
    Réponses: 2
    Dernier message: 06/02/2008, 19h07

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