D'abord, vous devez comprendre le mécanisme des modèles.Les modèles ne sont pas compilés, ils sont instanciés lorsqu'ils sont utilisés et ensuite leur instanciation est compilée. Le compilateur doit donc avoir la définition complète du modèle dans chaque module en utilisant la fonction template, afin de les instancier d'abord en fonction des paramètres que vous avez passés.
Pour résoudre votre problème, il existe trois solutions, mais vous verrez qu'elles aboutissent au même résultat. Soit vous mettre en œuvre vos modèles entiers dans votre fichier d'en-tête dans la définition de la classe (nous utilisons pour les suffixe avec .hxx au lieu de .h afin de préciser qu'ils contenant des définitions de modèles):
// Foo.hxx
#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
public:
template <class T>
void bar(const T& t) {
t.doSomething();
}
};
#endif
Ou vous pouvez la définition de extérioriser la classe, mais toujours dans le fichier d'en-tête:
// Foo.hxx
#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
public:
template <class T>
void bar(const T&);
};
template <class T>
void Foo::bar(const T& t) {
t.doSomething();
}
#endif
Enfin, vous pouvez mettre en œuvre des organismes de méthodes de modèle dans un fichier externe (préfixés par .cxx pour la même raison). Il contiendra les corps des méthodes mais n'inclura pas "Foo.hxx". Au lieu de cela, c'est "Foo.hxx" qui inclura "Foo.cxx" après la définition de la classe. De cette façon, lorsque le compilateur résout la directive #include, il trouve la définition entière du modèle dans le même module, ce qui lui permet instancier:
// Foo.hxx
#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
public:
template <class T>
void bar(const T&);
};
#include "Foo.cxx"
#endif
// Foo.cxx
template <class T>
void Foo::bar(const T& t) {
t.doSomething();
}
Le choix entre ces 3 façons de mettre en œuvre des modèles est plutôt une question de lisibilité (et goût).
Deuxième et troisième sont équivalents en termes de code généré, mais je préfère ne pas utiliser la solution de fichier cxx, car elle conduit souvent à des erreurs stupides lorsque vous oubliez d'inverser l'include. En outre, les bibliothèques C++ bien connues comme STL ou Boost proposent leur code uniquement dans les fichiers d'en-tête, ce qui est un signe de bon design. En utilisant la définition externe dans les en-têtes, vous clarifiez la définition de votre classe. Vous empêchez également le compilateur à automatiquement les méthodes en ligne, ce qui peut parfois conduire à des résultats médiocres selon Herb Sutter http://www.gotw.ca/gotw/033.htm
Dans le cas où un modèle est nécessaire dans un seul fichier CPP, alors je mettrais la déclaration et la définition dans ce fichier. S'il s'agit d'un modèle de fonction membre et que le reste de la classe est utilisé dans d'autres unités de traduction, je mettrai certainement la définition dans le fichier .h. Je serais même préoccupé par le comportement indéfini ou défini par l'implémentation si cela n'était pas fait. –
@ Marcus, j'aurais dû mentionner que dans la question d'origine - oui Foo :: doSomething() est une fonction membre de modèle privé de la classe Foo. –
@John, je ne suis pas votre point, pourriez-vous élaborer un peu? Dans mon cas, j'ai une fonction de membre de modèle privé de la classe Foo, donc elle est seulement utilisée par l'implémentation de la classe Foo dans Foo.cpp. Cela semble donc correspondre au premier cas/phrase dans votre commentaire. Mais ma situation correspond également à la description dans la deuxième phrase de votre commentaire ("modèle de fonction membre et le reste de la classe est utilisé dans d'autres unités de traduction") où vous dites de le mettre dans un fichier .h. –