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 :

Template et erreur de Link


Sujet :

C++

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2012
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2012
    Messages : 69
    Par défaut Template et erreur de Link
    Bonsoir,

    J'ai des classes template définies dans une lib statique.
    J'appelle ces classes dans un exe qui doit donc se linker avec la lib, et là j'ai une erreur, j'ai cherché je ne vois vraiment pas alors j'ai réécrit une partie de ma classe sans les template et là ça marche très bien, ça se link parfaitement.

    J'ai écrit un exemple simplifié qui illustre mon problème :
    La classe CVector (qui n'est pas template) qui marche très bien.
    La classe TVector (template) qui ne marche pas (problème de link.

    fichier d'en-tête de la lib statique :

    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
     
    #ifndef _VECTOR_H_
    #define _VECTOR_H_
     
    #include <vector>
     
    class CVector
    {
    protected:
      int				  m_nSize;
      std::vector<double>*	  m_pVec;
    public:
      CVector( );
      CVector( int size );
      ~CVector( ) { delete m_pVec; }
      void SetCoef( int i, double dValue ) { (*m_pVec)[i] = dValue; }
      double GetSumCoef( ) const
      {
    	double dSum = 0.0;
    	for ( int i = 0; i < m_nSize; i++ )
      	  dSum += m_pVec->at(i);
    	return dSum;
      }
    };
     
    template<typename CNumeric>
    class TVector
    {
    protected:
      int					m_nSize;
      std::vector<CNumeric>*	m_pVec;
    public:
      TVector( );
      TVector( int size );
      ~TVector( ) { delete m_pVec; }
      void SetCoef( int i, CNumeric dValue ) { (*m_pVec)[i] = dValue; }
      double GetSumCoef( ) const
      {
    	CNumeric dSum = 0.0;
    	for ( int i = 0; i < m_nSize; i++ )
    	  dSum += m_pVec->at(i);
    	return dSum;
      }
    };
     
    #endif // _VECTOR_H_
    Le cpp de la lib statique :

    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
     
    #include "stdafx.h"
    #include "vector.h"
     
    CVector::CVector( )
      : m_pVec(NULL), m_nSize(0)
    {
    }
     
    CVector::CVector( int size )
    {
      m_pVec = new std::vector<double>;
      for ( size_t i = 1; i <= size; i++ )
        m_pVec->insert(m_pVec->end(), 0.0);
      m_nSize = size;
    }
     
    template<typename CNumeric>
    TVector<CNumeric>::TVector( )
      : m_pVec(NULL), m_nSize(0)
    {
    }
     
    template<typename CNumeric>
    TVector<CNumeric>::TVector( int size )
    {
      m_pVec = new std::vector<CNumeric>;
      for ( size_t i = 1; i <= size; i++ )
        m_pVec->insert(m_pVec->end(), 0.0);
      m_nSize = size;
    }
    Et voici l'exécutable :

    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
     
    #include "stdafx.h"
    #include "vector.h"
     
    int _tmain(int argc, _TCHAR* argv[])
    {
      CVector vect(5);
      vect.SetCoef(0, 0);
      vect.SetCoef(1, 1);
      vect.SetCoef(2, 2);
      vect.SetCoef(3, 3);
      vect.SetCoef(4, 4);
      double dSum = vect.GetSumCoef();
      printf("sum = %f\n", dSum);
     
      TVector<double> tVect(5);
      tVect.SetCoef(0, 0);
      tVect.SetCoef(1, 1);
      tVect.SetCoef(2, 2);
      tVect.SetCoef(3, 3);
      tVect.SetCoef(4, 4);
      dSum = tVect.GetSumCoef();
      printf("sum = %f\n", dSum);
     
      return 0;
    }
    Comme je le disais, toute la partie sans template marche mais la partie avec template ne marche pas ... à croire qu'il faut mettre des options différentes dans les options du projet si on utilise des template ?

    Merci de m'aider.

    Bonne nuit.

  2. #2
    Membre émérite
    Avatar de mitkl
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2010
    Messages : 364
    Par défaut
    Mets tes constructeurs de TVector dans le header et non dans le fichier cpp.

  3. #3
    Membre Expert Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Par défaut
    Bonsoir
    Apparemment, tu as définit tes fonctions template dans un .cpp
    Ils doivent se trouver dans un fichier d'en-tête.
    http://cpp.developpez.com/faq/cpp/?p...VERS_templates

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2012
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2012
    Messages : 69
    Par défaut
    Ah OK merci beaucoup

    J'essaie ça dès demain !!

    Merci à vous !
    Bonne nuit.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 636
    Par défaut
    Salut,

    Une petite astuce qui pourrait s'avérer intéressante dans le cadre d'une bibliothèque (qu'elle soit statique ou dynamique):

    Comme Ehonn l'a fait remarquer, le compilateur a besoin de disposer de l'implémentation des fonctions template (ou des fonctions membres de classes template) au moment où elles sont appelées, et, cela pour une raison bien simple : il doit connaitre la taille des différents types utilisés, et ca, il ne peut les déduire qu'au moment où... on appelle la fonction.

    Il n'est cependant pas exclu ( c'est d'ailleurs même plus que probable ) que ta bibliothèque ne doive travailler qu'avec un nombre finalement très restreint de type comme par exemple : les types primitifs comme char, short, int, long, long long et leurs version unsigned ainsi que float, double et long double.

    Il ne semble en effet pas particulièrement cohérent de se dire que l'on pourrait invoquer la fonction membre GetSumCoef sur une spécialisation de ta classe n'utilisant pas... des valeurs numériques

    Tout cela pour dire que si tu forces le compilateur à créer le code binaire correspondant à l'ensemble des types que ta bibliothèque est susceptible d'utiliser, il pourra le l'insérer (le code binaire) directement dans la bibliothèque et donc éviter l'erreur à l'édition de liens, car l'implémentation des fonctions sera effectivement disponible, en se contentant de fournir... la déclaration des fonctions membres à l'utilisateur.

    ET, pour cela, C++ est un langage génial, car il permet de mettre cette idée en pratique

    Le principe utilisé prend comme nom celui de l'instanciation explicite

    Un petit exemple pour te permettre de comprendre...

    Mettons que tu aies une classe et une fonction template qui sont déclarées dans un fichier d'en-tête sous les formes de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <typename T>
    class MyClass
    {
        public:
            /* je passe les constructeurs et les destructeurs */
            void function_one();
            Type /* const & */ getter() const;
            /* ...*/
    };
    template <typename T>
    void bar(T t);
    et dont les implémentations sont fournies dans un fichier séparé (idéalement, on évitera l'extension .cpp car on n'inclut jamais un fichier cpp, mais une extension possible pourrait etre .impl ou n'importe quel autre à ton gout ) sous la forme de
    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
    template <typename T>
    void MyClass<T>::function_one()
    {
        /* ...*/
    }
    template <typename T>
    Type /* const & */ MyClass<T>::getter() const
    {
        /* ... */
    }
    ;
    template <typename T>
    void bar<T>( T t)
    {
     /* ... */
    }
    Tu pourrais ne donner que le fichier d'en-tête à l'utilisateur et forcer le compilateur à générer le code pour l'ensemble des types primitifs en créant un fichier .cpp qui prendrait la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <l_entete.h>
    #include <l_implmentation.impl>
    /* instancie l'ensemble de la classe, y compris les fonctions membres */
    template class MyClass<char>; // pour le type char
    template class MyClass<short>; // pour le type short
    template class MyClass<int>; // pour le type int
    /*...*/
    /* instancie la fonction foo */
    template void foo<char>(char); // pour le type char
    template void foo<short>(char); // pour le type short
    template void foo<int>(char); // pour le type int
    /*...*/
    L'utilisateur n'obtiendra alors une erreur de link que s'il essaye... d'utiliser un des types pour lesquels tu n'as pas fait l'instanciation explicite
    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

  6. #6
    Membre Expert

    Avatar de germinolegrand
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2010
    Messages
    738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Octobre 2010
    Messages : 738
    Par défaut
    (idéalement, on évitera l'extension .cpp car on n'inclut jamais un fichier cpp, mais une extension possible pourrait etre .impl ou n'importe quel autre à ton gout )
    Une extension courante est .inl (code::blocks la reconnait comme étant une source C++ non compilée).

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 636
    Par défaut
    Citation Envoyé par germinolegrand Voir le message
    Une extension courante est .inl (code::blocks la reconnait comme étant une source C++ non compilée).
    En fait, tu es totalement libre du choix de l'extension...

    Gcc, par exemple, utilise tcc (et on la retrouve aussi sur d'autres projets d'envergure et on trouve aussi assez facilement des extension imp ou autres sur certains projets

    Tu pourrais d'ailleurs tout à fait utiliser l'extension .h ou .hpp si tu le voulais, bien que cela puisse porter à confusion (au meme titre que .cpp d'ailleurs)

    Le principal étant surtout que tu garde une certaine logique dans le choix de l'extension, et que tu t'y conformes sur l'ensemble du projet, pour éviter d'avoir 36 extensions différentes
    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

  8. #8
    Membre Expert

    Avatar de germinolegrand
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Octobre 2010
    Messages
    738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Octobre 2010
    Messages : 738
    Par défaut
    Je suis tout à fait d'accord, on peut utiliser .monextensionpersoquepersonneconnait
    Mais simplement pour le confort d'utilisation sous windows, je recommande cette extension .inl qui est reconnue par c::b comme une source (c'est à dire que dans l'explorateur windows on la même belle icone que les .cpp) mais non compilée, ce qui signifie que lorsque l'on ajoute un tel fichier à un projet .cbp, c::b proposera immédiatement les bonnes options pour le fichier (c'est à dire pas compilé et pas linké non plus).

    Après c'est seulement une des nombreuses astuces qui facilitent la vie du développeur C++ sous windows

  9. #9
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Le principe utilisé prend comme nom celui de l'instanciation explicite
    Attention, l'instanciation explicite présente des inconvénients aussi. Le plus gros de ceux-ci est qu'il tente d'instancier l'ensemble des fonctions membre d'une classe, au lieu de seulement celles utilisées. Ce qui est contraire à la philosophie du C++ où selon les fonctionnalités du type sous-jacent, le template est plus ou moins riche. Ici, on essaye d'aller au plus riche, et si on ne peut pas, on bloque tout.

    Par exemple, std::list possède une fonction sort, qui a besoin d'une comparaison entre les membres. Rien n'empêche d'avoir une liste d'éléments sans cette comparaison, tant qu'on n'essaye pas de la trier. Avec l'instanciation explicite, il faut à tout prix pouvoir comparer les éléments...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    list <int> l1; // Ok
    list <complex<double> > l2; // Ok
    template class list<int>; // Ok
    template class list<complex<double> >; // KO, alors qu'il n'y a vraiment pas de quoi !
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2012
    Messages
    69
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2012
    Messages : 69
    Par défaut
    Bonsoir à tous et merci pour vos réponses, j'apprends plein de choses !

    Bon j'ai essayé plusieurs choses, déjà je mettre l'implémentation dans mes .h et ça marche ! Youpi !

    Par contre j'ai aussi essayé de mettre l'implémentation dans un .tpp mais là j'ai des erreurs à la compilation, pourtant ça m'avait l'air clair dans l'exemple de la faq, à la différence que j'ai des #include "stdafx.h" et c'est peut-être pas supporté dans un .tpp ? Pourtant si je l'enlève le compilo me crie dessus.

    Bon je vous montre le code mais je ré-essaie demain, y'a pas de raison ça ne doit pas être sorcier de déplacer un bout de code.

    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
    // vector.h
     
    #ifndef _VECTOR_H_
    #define _VECTOR_H_
     
    #include <vector>
     
    template<typename CNumeric>
    class TVector
    {
    protected:
    	int						m_nSize;
    	std::vector<CNumeric>*		m_pVec;
    public:
    	TVector( );
    };
     
    #include "vector.tpp"
    #endif // _VECTOR_H_
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // vector.tpp
     
    #include "stdafx.h"
    #include "vector.h"
     
    template<typename CNumeric>
    TVector<CNumeric>::TVector( )
       : m_pVec(NULL), m_nSize(0) { }
    et il me dit :
    error C2995: 'TVector<CNumeric>::TVector(void)'*: modèle de fonction déjà défini

    Citation Envoyé par koala01 Voir le message
    Salut,
    Il n'est cependant pas exclu ( c'est d'ailleurs même plus que probable ) que ta bibliothèque ne doive travailler qu'avec un nombre finalement très restreint de type comme par exemple : les types primitifs comme char, short, int, long, long long et leurs version unsigned ainsi que float, double et long double.

    Il ne semble en effet pas particulièrement cohérent de se dire que l'on pourrait invoquer la fonction membre GetSumCoef sur une spécialisation de ta classe n'utilisant pas... des valeurs numériques

    Le principe utilisé prend comme nom celui de l'instanciation explicite
    Oui en effet mes libs vont rester numériques !

    Bon j'essaie de faire de "l'instanciation explicite" dès ce week-end.

    Merci à tous, je vous tiens au courant.

    Bonne nuit.

  11. #11
    Membre émérite
    Avatar de mitkl
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2010
    Messages : 364
    Par défaut
    n'inclut pas "vector.h" dans ton tpp

Discussions similaires

  1. template dans header mais erreur au link
    Par ctxnop dans le forum Langage
    Réponses: 1
    Dernier message: 11/12/2013, 15h20
  2. Erreur de link avec les templates
    Par suiss007 dans le forum C++
    Réponses: 6
    Dernier message: 04/01/2007, 11h09
  3. template et erreur de link
    Par Willand dans le forum Langage
    Réponses: 12
    Dernier message: 05/12/2006, 21h19
  4. Réponses: 15
    Dernier message: 21/08/2006, 01h41
  5. DirectDrawCreate erreur de link
    Par Ing_Ben dans le forum DirectX
    Réponses: 1
    Dernier message: 01/12/2002, 18h46

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