web-dev-qa-db-fra.com

Un problème de modèle provoque une erreur de l'éditeur de liens (C ++)

J'ai très peu d'idée de ce qui se passe en ce qui concerne les modèles C++, mais j'essaie d'implémenter une fonction qui recherche dans un vecteur un élément satisfaisant une propriété donnée (dans ce cas, en recherchant un avec le nom donné). Ma déclaration dans mon fichier .h est la suivante:

template <typename T>
T* find_name(std::vector<T*> v, std::string name);

Lorsque je compile, j'obtiens cette erreur de l'éditeur de liens lorsque j'appelle la fonction:

Error   1   error LNK2019: unresolved external symbol "class Item * __cdecl find_name<class Item>(class std::vector<class Item *,class std::allocator<class Item *> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??$find_name@VItem@@@@YAPAVItem@@V?$vector@PAVItem@@V?$allocator@PAVItem@@@std@@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z) referenced in function "public: class Item * __thiscall Place::get_item(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?get_item@Place@@QAEPAVItem@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) place.obj   Program2

Encore une fois, je suis nouveau dans les modèles, donc je ne sais pas ce qui se passe. Toutes les instances que j'ai trouvées de LNK2019 via Google concernaient l'utilisation de bibliothèques incorrectes, mais comme c'est ma propre fonction, je ne vois pas pourquoi cela se produirait.

En outre, une question connexe: existe-t-il un moyen de créer un paramètre de modèle de sorte qu'il doit être une sous-classe d'une certaine classe, c'est-à-dire un modèle?

42
marsolk

Vous devez avoir vos définitions de modèle disponibles sur le site appelant. Cela signifie que non .cpp des dossiers.

La raison en est que les modèles ne peuvent pas être compilés. Considérez les fonctions comme des cookies et le compilateur est un four.

Les modèles ne sont qu'un emporte-pièce, car ils ne savent pas de quel type de cookie il s'agit. Il indique uniquement au compilateur comment créer la fonction lorsque l'on lui donne un type, mais en soi, il ne peut pas être utilisé car aucun type concret n'est utilisé. Vous ne pouvez pas faire cuire un emporte-pièce. Ce n'est que lorsque vous avez la savoureuse pâte à biscuits prête (c'est-à-dire, étant donné le compilateur la pâte [type])) que vous pouvez couper le cookie et le faire cuire.

De même, ce n'est que lorsque vous utilisez réellement le modèle avec un certain type que le compilateur peut générer la fonction réelle et la compiler. Cependant, il ne peut pas le faire si la définition du modèle est manquante. Vous devez le déplacer dans le fichier d'en-tête, afin que l'appelant de la fonction puisse créer le cookie.

73
GManNickG

Vous souffrez probablement de manquer une instanciation valide. Si vous placez votre définition de modèle dans un fichier .cpp distinct, lorsque le compilateur compile ce fichier, il peut ne pas savoir de quelles instanciations vous avez besoin. Inversement, sur les sites d'appel qui instancieraient la version correcte de la fonction de modèle, si la définition du corps de la fonction n'est pas disponible, le compilateur n'aura pas les informations pour instancier les spécialisations requises.

Vous avez deux options. Placez le corps de la fonction pour le modèle de fonction dans le fichier d'en-tête.

par exemple. dans le fichier d'en-tête:

template <typename T>
inline T* find_name(std::vector<T*> v, std::string name)
{
    // ...
}

ou instancier explicitement le modèle dans le .cpp où vous avez défini le modèle.

par exemple. dans le fichier source (nécessitera probablement #includeing le fichier qui définit Item):

template <typename T>
T* find_name(std::vector<T*> v, std::string name)
{
    // ...
}

template Item* find_name<Item>(std::vector<Item*> v, std::string name);
47
CB Bailey

Les réponses ici sont excellentes.

J'ajouterai simplement que c'est souvent la raison pour laquelle en plus de .h et .cpp fichiers dans un projet. Vous trouverez souvent .inl des dossiers. Les définitions de modèle iront dans le .inl fichier.

Celles-ci .inl les fichiers signifient en ligne et seront généralement inclus par le .h fichier du même préfixe de nom au bas du fichier après toutes les déclarations d'en-tête. Cela les fait effectivement partie du fichier d'en-tête mais sépare les déclarations de toutes les définitions.

Comme ce sont des fichiers d'en-tête glorifiés, vous devez prendre toutes les mêmes précautions que vous le feriez avec un fichier d'en-tête normal, c'est-à-dire inclure des gardes, etc.

11
chollida

Nous sommes tombés sur le même problème et avons trouvé ceci qui indique 3 solutions: http://www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-ah-file-and -imp

Parmi celles-ci, une méthode simple consiste à créer une méthode "factice" dans le fichier .cpp, qui appelle la fonction modèle/classe avec les différents types. Collé à partir du lien:

// No need to call this TemporaryFunction() function, it's just to avoid link error.
void TemporaryFunction ()
{
    TestTemp<int> TempObj;
    TestTemp<float> TempObj2;
}
5
KBog

Je viens de remarquer que vous aviez une deuxième question qui semble sans réponse:

Existe-t-il un moyen de créer un paramètre de modèle de sorte qu'il doit être une sous-classe d'une certaine classe, c'est-à-dire un modèle?

C'est possible. Par exemple, voir is_base_of dans Boost.TypeTraits .

Cependant, je suis curieux: pourquoi voulez-vous cela? Normalement, les exigences d'un modèle sur ses paramètres ne concernent pas le type du paramètre lui-même, mais les expressions impliquant ce type sont légales. Par exemple, imaginez que vous avez:

template<class T>
void foo(const T& t)
{
    if (t.foo()){
       t.bar("blah");
    }
}

Dire que T doit hériter de quelque chose comme:

class HasFooAndBar
{
public:
  void foo()const;
  void bar(const char*)const;
};

n'apporte rien car l'instanciation de la fonction échouera quand même si le type ne supporte pas les opérations. De plus, cela limite inutilement l'applicabilité de foo(). En fait, toutes les exigences de foo sont que t.foo()and t.bar(const char*) sont des expressions valides sur un const T. Par exemple, ce type n'hérite pas de HasFooAndBar et est toujours un paramètre foo () valide:

struct DifferentFromHasFooAndBar
{
  bool foo()const;
  std::string bar(const std::string&)const;
};
1
Éric Malenfant

Avez-vous mis votre définition de fonction de modèle dans un fichier cpp? Déplacez-le ensuite vers l'en-tête et insérez-le.

0
dirkgently