OVH Cloud OVH Cloud

Classe Template

4 réponses
Avatar
sej
Bonjour,

J'ai une classe template et j'ai une erreur au linkage.
Si je déclare le contructeur de la classe Test en inline ou dans le
fichier main.cpp ça passe sinon j'ai l'erreur suivante sous Visual .Net
2003 :

Compilation...
main.cpp
Édition des liens en cours...
main.obj : error LNK2019: symbole externe non résolu "public: __thiscall
Test<char>::Test<char>(class std::basic_string<char,struct
std::char_traits<char>,class std::allocator<char> >,char)"
(??0?$Test@D@@QAE@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@D@Z)
référencé dans la fonction _main
main.obj : error LNK2019: symbole externe non résolu "public: __thiscall
Test<int>::Test<int>(class std::basic_string<char,struct
std::char_traits<char>,class std::allocator<char> >,int)"
(??0?$Test@H@@QAE@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z)
référencé dans la fonction _main
Debug/template.exe : fatal error LNK1120: 2 externes non résolus


-----------------------------------
// Template.h
#ifndef TEMPLATE_H
#define TEMPLATE_H


#include <iostream>
#include <string>

using namespace std;


template<class T>
class Test
{
public:
Test (string name, T val);// {_val = val; cout <<
"construct name=" << name << ", _val=" << _val << endl;};

T GetVal (void) {return _val; };
void SetVal (T val) {_val = val; };
int Size (void) {return sizeof(_val); };

private:
T _val;


};

#endif

--------------------------------------
// Template.cpp
// template.cpp : définit le point d'entrée pour l'application console.
//

#include <iostream>
#include <string>

#include "template.h"

using namespace std;



template<class T>
Test<T>::Test(string name, T val)
{
_val = val; cout << "construct name=" << name << ", _val=" << _val
<< endl;
}


---------------------------------------
// Main.cpp
// template.cpp : définit le point d'entrée pour l'application console.
//

#include <iostream>
#include <string>

#include "template.h"

using namespace std;




int main(int argc, char **)
{
Test<int> t("toto", 9);
t.SetVal(5);
cout << t.GetVal() << endl;
cout << t.Size() << endl;

Test<char> c("test", 'Z');
c.SetVal('0');
cout << c.GetVal() << endl;
cout << c.Size() << endl;


return 0;
}

4 réponses

Avatar
sej
Ca marche si je mets la définition du constructeur et des fonctions
memebres dans le .h en dessous de la définition de la classe.
La question est donc : est-ce que le compilo sait linker une classe
template compilée, si une classe template est compilable ? ou est-ce que
le compilo interprète le .h pour générer les classes ?


Bonjour,

J'ai une classe template et j'ai une erreur au linkage.
Si je déclare le contructeur de la classe Test en inline ou dans le
fichier main.cpp ça passe sinon j'ai l'erreur suivante sous Visual .Net
2003 :

Compilation...
main.cpp
Édition des liens en cours...
main.obj : error LNK2019: symbole externe non résolu "public: __thiscall
Test<char>::Test<char>(class std::basic_string<char,struct
std::char_traits<char>,class std::allocator<char> >,char)"
(??0?$@@?$?$@std@@V?$@2@@std@@)
référencé dans la fonction _main
main.obj : error LNK2019: symbole externe non résolu "public: __thiscall
Test<int>::Test<int>(class std::basic_string<char,struct
std::char_traits<char>,class std::allocator<char> >,int)"
(??0?$@@?$?$@std@@V?$@2@@std@@)
référencé dans la fonction _main
Debug/template.exe : fatal error LNK1120: 2 externes non résolus


-----------------------------------
// Template.h
#ifndef TEMPLATE_H
#define TEMPLATE_H


#include <iostream>
#include <string>

using namespace std;


template<class T>
class Test
{
public:
Test (string name, T val);// {_val = val; cout <<
"construct name=" << name << ", _val=" << _val << endl;};

T GetVal (void) {return _val; };
void SetVal (T val) {_val = val; };
int Size (void) {return sizeof(_val); };

private:
T _val;


};

#endif

--------------------------------------
// Template.cpp
// template.cpp : définit le point d'entrée pour l'application console.
//

#include <iostream>
#include <string>

#include "template.h"

using namespace std;



template<class T>
Test<T>::Test(string name, T val)
{
_val = val; cout << "construct name=" << name << ", _val=" << _val
<< endl;
}


---------------------------------------
// Main.cpp
// template.cpp : définit le point d'entrée pour l'application console.
//

#include <iostream>
#include <string>

#include "template.h"

using namespace std;




int main(int argc, char **)
{
Test<int> t("toto", 9);
t.SetVal(5);
cout << t.GetVal() << endl;
cout << t.Size() << endl;

Test<char> c("test", 'Z');
c.SetVal('0');
cout << c.GetVal() << endl;
cout << c.Size() << endl;


return 0;
}


Avatar
Jean-Marc Bourguet
sej writes:

J'ai une classe template et j'ai une erreur au linkage.
Si je déclare le contructeur de la classe Test en inline ou dans le
fichier main.cpp ça passe sinon j'ai l'erreur suivante sous Visual .Net
2003 :


Gaby, je crois que c'est un bon candidat pour la FAQ...

La norme définit deux modèles de compilation. Ces deux
modèles partagent certaines choses -- dont la recherche des
noms en deux phases -- et diffèrent principalement sur la
manière dont la définition d'un template est trouvée
lorsqu'une instantiation est nécessaire.

* le modèle d'inclusion où dès qu'un template est
instantié dans une unité de compilation il faut que la
déclaration y soit aussi présente. Ce modèle est
supporté par la totalité des compilateurs (qui
généralement ne diagnostiquent pas le fait que la
déclaration est absente et se contentent alors de ne pas
générer les instantiations).

* le modèle de compilation séparée où la déclaration doit
etre marquée avec "export" (pas explicitement d'après la
norme mais je crois qu'il y a un DR sur le sujet et
c'est bien nécessaire pour les implémentations
disponibles) et la définition doit être fournie dans une
seule unite de compilation. À ma connaissance, une
seule implémentation est disponible, celle d'EDG qui est
disponible à travers les compilateurs de Comeau et
d'Intel.

Ca marche si je mets la définition du constructeur et des
fonctions memebres dans le .h en dessous de la définition
de la classe. La question est donc : est-ce que le
compilo sait linker une classe template compilée, si une
classe template est compilable ?


Les deux compilateurs disposant du modèle de compilation
séparée pour les templates ont besoin que les définitions
soient disponibles au moment de l'édition de lien et génèrent
le code à ce moment-là si c'est nécessaire. Ils pouraient les
compiler en quelque chose ressemblant aux en-têtes
pré-compilés mais ce n'est pas quelque chose de disponible.
Faire des templates avec du code partagé n'est vraiment pas
pratique en C++ (par exemple si on a
template <typename T> void f();
f<int> et f<long> doivent avoir des adresses différentes)


ou est-ce que le compilo interprète le .h pour générer les
classes ?


Ça marche avec tout les compilateurs.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
sej
Merci pour ces informations, je ne connaissais pas cette FAQ.
J'ai regardé sur la MSDN et l' "export" n'est pas géré par le
compilateur de Visual .Net donc je reste sur la solution des définitions
dans le .h

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrf14exportkeywordontemplate.asp:

The export keyword is not supported on templates. For example, the
following sample will not compile:

export template <class T> void fun(T);
export template <class T> class A;


sej writes:


J'ai une classe template et j'ai une erreur au linkage.
Si je déclare le contructeur de la classe Test en inline ou dans le
fichier main.cpp ça passe sinon j'ai l'erreur suivante sous Visual .Net
2003 :



Gaby, je crois que c'est un bon candidat pour la FAQ...

La norme définit deux modèles de compilation. Ces deux
modèles partagent certaines choses -- dont la recherche des
noms en deux phases -- et diffèrent principalement sur la
manière dont la définition d'un template est trouvée
lorsqu'une instantiation est nécessaire.

* le modèle d'inclusion où dès qu'un template est
instantié dans une unité de compilation il faut que la
déclaration y soit aussi présente. Ce modèle est
supporté par la totalité des compilateurs (qui
généralement ne diagnostiquent pas le fait que la
déclaration est absente et se contentent alors de ne pas
générer les instantiations).

* le modèle de compilation séparée où la déclaration doit
etre marquée avec "export" (pas explicitement d'après la
norme mais je crois qu'il y a un DR sur le sujet et
c'est bien nécessaire pour les implémentations
disponibles) et la définition doit être fournie dans une
seule unite de compilation. À ma connaissance, une
seule implémentation est disponible, celle d'EDG qui est
disponible à travers les compilateurs de Comeau et
d'Intel.


Ca marche si je mets la définition du constructeur et des
fonctions memebres dans le .h en dessous de la définition
de la classe. La question est donc : est-ce que le
compilo sait linker une classe template compilée, si une
classe template est compilable ?



Les deux compilateurs disposant du modèle de compilation
séparée pour les templates ont besoin que les définitions
soient disponibles au moment de l'édition de lien et génèrent
le code à ce moment-là si c'est nécessaire. Ils pouraient les
compiler en quelque chose ressemblant aux en-têtes
pré-compilés mais ce n'est pas quelque chose de disponible.
Faire des templates avec du code partagé n'est vraiment pas
pratique en C++ (par exemple si on a
template <typename T> void f();
f<int> et f<long> doivent avoir des adresses différentes)



ou est-ce que le compilo interprète le .h pour générer les
classes ?



Ça marche avec tout les compilateurs.

A+




Avatar
Jean-Marc Bourguet
writes:

Jean-Marc Bourguet wrote:

[...]
* le modèle de compilation séparée où la déclaration doit
etre marquée avec "export" (pas explicitement d'après la
norme mais je crois qu'il y a un DR sur le sujet et
c'est bien nécessaire pour les implémentations
disponibles) et la définition doit être fournie dans une
seule unite de compilation. À ma connaissance, une
seule implémentation est disponible, celle d'EDG qui est
disponible à travers les compilateurs de Comeau et
d'Intel.


Encore : est-ce que le fait que la définition doit être fournie dans
une seule unité de compilation est imposé par la norme, ou est-ce
que c'est simplement un contrait d'une implémentation donnée (qui
est en l'occurance la seule) ?


Apres une relecture rapide de l'ODR, je ne sais pas si la norme le
demande ou pas et je n'ai pas le temps de regarder ca plus en
profondeur. En fait je ne suis meme pas sur qu'EDG le verifie. Je
verrai ca chez moi si j'ai le temps. J'avais simplement extrapole --
peut-etre un peu vite -- l'ODR des fonctions aux templates exportes.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org