[C++]Edition de lien pour template
Résolu/Fermé
A voir également:
- [C++]Edition de lien pour template
- Créer un lien pour partager des photos - Guide
- Lien url - Guide
- Verifier un lien - Guide
- Aucune application permettant d'ouvrir ce lien n'a été trouvée - Forum Mobile
- Everest home edition - Télécharger - Informations & Diagnostic
3 réponses
Char Snipeur
Messages postés
9813
Date d'inscription
vendredi 23 avril 2004
Statut
Contributeur
Dernière intervention
3 octobre 2023
1 298
14 mars 2007 à 08:34
14 mars 2007 à 08:34
Salut.
Il faut TOUT mettre dans le même fichier.
en template, comme tu compile à chaque fichier, il faut mettre déclaration et implémentation dans le .hpp
et ton erreur viens surtout du fait que tu fait
Il faut TOUT mettre dans le même fichier.
en template, comme tu compile à chaque fichier, il faut mettre déclaration et implémentation dans le .hpp
et ton erreur viens surtout du fait que tu fait
template <typename T> Exemple<T>::Exemple(){ std::cout << "ceci est un exemple" << std::endl; }dans le .c, alors qu'il est déjà défini dans le .tpp inclu dans le .hh
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
14 mars 2007 à 16:28
14 mars 2007 à 16:28
Ca n'as aucun sens de créer un .o d'un fichier contenant des fonctions templates, c'est ce que je t'ai expliqué dans le premier post, car tu ne sais pas pour quels types la classe template va être instanciée. Il faut donc mettre le code des fonction template dans le header (.hpp). Si tu mets les fonctions templates dans un fichier séparé, celui-ci doit être inclu de la même façon qu'un .hpp.
Je t'invite à repartir des exemples que je t'ai donné, et de prendre le temps de comprendre ce que j'ai baragouiné la fois précédente.
Bonne chance
Je t'invite à repartir des exemples que je t'ai donné, et de prendre le temps de comprendre ce que j'ai baragouiné la fois précédente.
Bonne chance
mamiemando
Messages postés
33446
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
20 décembre 2024
7 812
14 mars 2007 à 01:46
14 mars 2007 à 01:46
Introduction
Concrètement un fichier tcc peut être vu comme un .h car les classes templates ne peuvent être compilées directement, car tu ne sais pas pour quel(s) type(s) elle sera utilisée (et dans le cas générique ça peut être n'importe quel type !).
Ce qu'il faut bien voir c'est que concrètement pour une classe template donnée, celle-ci sera compilée pour chaque type;
Personnellement, je mets simplement les classes et les fonctions templates dans les .hpp mais tu peux les mettre à part dans un tcc.
Contrairement aux fonctions classiques implémentées dans le .hpp, tu n'as pas besoin d'utiliser le mot clé inline (qui évite les multidéfinitions des fonctions implémentées dans le .hpp si celui-ci est inclu à plusieurs endroits). Dans l'exemple ci dessous je détaille à quoi sert le mot clé inline.
Un exemple
plop.hpp
plop.cpp
Concrètement que se passe-t'il ?
Pour insister sur le problème des inline si tu considères les fichiers :
- main.cpp, a.cpp, b.cpp
- a.hpp, (inclu par a.cpp et main.cpp)
- b.hpp, (inclu par b.cpp et main.cpp)
- plop.hpp (inclu par a.cpp et b.cpp)
Dans a.o tu auras compilé les codes des fonctions non template de a.hpp et plop.hpp.
Dans b.o tu auras compilé les codes des fonctions non template de b.hpp et plop.hpp.
Dans main.o tu auras compilé les codes des fonctions non template de a.hpp, b.hpp et plop.hpp.
Ne perds pas de vue que les fonctions inline n'ont pas de symbole proprement dit car elles sont directement remplacée par leur code à chaque appel, et donc tu ne pourras jamais avoir de multidéfinition sur ces fonctions là.
Au moment de construire l'exécutable, tu vas linker a.o, b.o et main.o. Si certaines fonctions apparaissent dans plusieurs .o ça fait une multidéfinition (typiquement une fonction non template et non inline définie dans a.hpp (respectivement b.hpp) car celle-ci sera compilée dans a.o (respectivement b.cpp) et main.o. Et ce malgré les verrous que tu as pu mettre dans a.hpp et b.hpp !
Si je n'ai pas été claire n'hésite pas à me dire ce que tu n'as pas compris.
Bonne chance
Concrètement un fichier tcc peut être vu comme un .h car les classes templates ne peuvent être compilées directement, car tu ne sais pas pour quel(s) type(s) elle sera utilisée (et dans le cas générique ça peut être n'importe quel type !).
Ce qu'il faut bien voir c'est que concrètement pour une classe template donnée, celle-ci sera compilée pour chaque type;
#include <vector> // dans ce header se trouve la classe template std::vector int main(){ std::vector<int> v; // => compilation de std::vector<int> std::vector<double> w; // => compilation de std::vector<double> return 0; }
Personnellement, je mets simplement les classes et les fonctions templates dans les .hpp mais tu peux les mettre à part dans un tcc.
Contrairement aux fonctions classiques implémentées dans le .hpp, tu n'as pas besoin d'utiliser le mot clé inline (qui évite les multidéfinitions des fonctions implémentées dans le .hpp si celui-ci est inclu à plusieurs endroits). Dans l'exemple ci dessous je détaille à quoi sert le mot clé inline.
Un exemple
plop.hpp
#ifndef PLOP //verrou pour n'inclure ce fichier qu'une fois... par .o ! #define PLOP #include <iostream> #include <vector> // 1er exemple : une fonction template // Recherche le maximum dans un vector template <typename T> std::pair<T,bool> max(const std::vector<T> & v){ // je vérifie que le vector est non vide pour pouvoir utiliser v[0] ensuite if (v.empty()) return std::make_pair(res,false); T res = v[0]; typename std::vector<T>::const_iterator vit = v.begin(), vend = v.end(); // Le typename est important car on accède (::) à un champ (const_iterator) // de la classe std::vector<T> qui dépend d'un paramètre template. // je parcours le vector pour trouver le maximum for(;vit!=vend;++vit){ if(*vit > res) res = *vit; } return std::make_pair(res,true); } // 2eme exemple : une classe/structure template template <typename T> struct ma_classe(){ T t; ma_classe(){} void print() const{ std::cout << t << std::endl; } }; // 3eme exemple : une méthode template class plop{ protected int x; // Le constructeur plop(int x0=0):x(x0){}; // Cette méthode est implémentée dans le .hpp il faut donc la mettre inline // En général on ne met que des méthodes "triviales" et courtes en inline car // concrètement le inline recopie le code de la fonction inline à chaque appel // de cette méthode. Si tu ne mets pas le inline et que ce fichier est inclu // par a.cpp et b.cpp, le code de la fonction serait présent dans a.o et b.o, // et au moment de linker tu aurais une multidéfinition de cette méthode. inline int get_x() const{ return x; } // Méthode template template <Tstream> Tstream & operator << (Tstream & out) const{ out << x; return out; } // Méthode déclarée dans le .hpp, implémentées dans le .cpp void set_x(int x0); }; // Comme toute fonction déclarée et implémentée dans un .hpp, je dois mettre // un inline car sinon j'aurais une multidéfinition de cette fonction si plop.hpp // est inclu à plusieurs endroits inline void ecrire_tapir(){ std::cout << "tapir" << std::endl; } #endif
plop.cpp
void plop::set_x(int x0){ x = x0; }
Concrètement que se passe-t'il ?
Pour insister sur le problème des inline si tu considères les fichiers :
- main.cpp, a.cpp, b.cpp
- a.hpp, (inclu par a.cpp et main.cpp)
- b.hpp, (inclu par b.cpp et main.cpp)
- plop.hpp (inclu par a.cpp et b.cpp)
Dans a.o tu auras compilé les codes des fonctions non template de a.hpp et plop.hpp.
Dans b.o tu auras compilé les codes des fonctions non template de b.hpp et plop.hpp.
Dans main.o tu auras compilé les codes des fonctions non template de a.hpp, b.hpp et plop.hpp.
Ne perds pas de vue que les fonctions inline n'ont pas de symbole proprement dit car elles sont directement remplacée par leur code à chaque appel, et donc tu ne pourras jamais avoir de multidéfinition sur ces fonctions là.
Au moment de construire l'exécutable, tu vas linker a.o, b.o et main.o. Si certaines fonctions apparaissent dans plusieurs .o ça fait une multidéfinition (typiquement une fonction non template et non inline définie dans a.hpp (respectivement b.hpp) car celle-ci sera compilée dans a.o (respectivement b.cpp) et main.o. Et ce malgré les verrous que tu as pu mettre dans a.hpp et b.hpp !
Si je n'ai pas été claire n'hésite pas à me dire ce que tu n'as pas compris.
Bonne chance
Bonsoir,
désolé mais j'ai du mal à comprendre comment ça se passe.
J'ai tenté de faire avec un exemple basique mais ça ne fonctionne pas
A la compilation de l'exemple, j'obtiens ce message :
Est-ce que tu pourrais m'indiquer dans l'exemple que j'ai écrit comment se corrige l'erreur
Merci
Voici les différents fichier et le Makefile
désolé mais j'ai du mal à comprendre comment ça se passe.
J'ai tenté de faire avec un exemple basique mais ça ne fonctionne pas
A la compilation de l'exemple, j'obtiens ce message :
g++ -o exemples.o -c exemples.cc -W -Wall -g exemples.cc:7: erreur: redefinition of ‘Exemple<T>::Exemple()’ exemples.tpp:4: erreur: ‘Exemple<T>::Exemple()’ previously declared here make: *** [exemples.o] Erreur 1
Est-ce que tu pourrais m'indiquer dans l'exemple que j'ai écrit comment se corrige l'erreur
Merci
Voici les différents fichier et le Makefile
// exemple.hh #ifndef EXEMPLE_HH #define EXEMPLE_HH template <typename T> class Exemple { public: Exemple(); }; #include "exemples.tpp" #endif // exemple.tpp template <typename T> Exemple<T>::Exemple() { } #include <iostream> #include "exemples.hh" template <typename T> Exemple<T>::Exemple(){ std::cout << "ceci est un exemple" << std::endl; } int main(){ Exemple<int> ex; return 0; } CC=g++ CFLAGS=-W -Wall -g LDFLAGS=-W -Wall -g EXEC=exo all: $(EXEC) exo: exemples.o $(CC) -o exo exemples.o $(LDFLAGS) exemples.o: exemples.cc $(CC) -o exemples.o -c exemples.cc $(CFLAGS) clean: rm -rf *.o mrproper: clean rm -rf $(EXEC)