2010-07-16 1 views
4

Ce problème consiste à définir et à déclarer un modèle de fonction dans un espace de noms défini dans un fichier externe d'instanciation. Voici le plus petit exemple reproductible que je pourrais trouver. 4 fichiers suivent:Le modèle de fonction dans un espace de noms dans un fichier séparé compile bien, mais l'éditeur de liens ne le trouve pas

La déclaration de modèle de fonction dans un espace de noms nommé:

// bar.h 
#include <algorithm> 

namespace barspace { 

    template <typename Iter> 
    void DoSomething (Iter first, Iter last); 

} 

La définition du modèle de fonction dans un fichier séparé:

// bar.cpp 
#include "bar.h" 

namespace barspace { 

    template <typename Iter> 
    void DoSomething (Iter first, Iter last) { 
     typedef typename std::iterator_traits<Iter>::value_type val_t; 
     std::sort (first, last); 
    } 

} // namespace barspace 

L'en-tête pour le programme principal

// foo.h 
#include "bar.h" 
#include <vector> 

Enfin, le programme principal où le modèle de fonction est appelé:

//foo.cpp 
#include "foo.h" 

int main() { 

    std::vector<double> v_d; 
    for (int i = 0; i < 10; i++) { 
    v_d.push_back (i); 
    } 

    barspace::DoSomething (v_d.begin(), v_d.end()); 

    return 0; 
} 

Je compilez comme suit:

g++ -c -o bar.o bar.cpp

g++ -c -o foo.o foo.cpp

Ces fonctionnent très bien. Maintenant, pour relier:

g++ bar.o foo.o -o foobar

Et l'erreur du compilateur résultant de la référence non définie:

foo.o: In function `main': 
foo.cpp:(.text+0x6e): undefined reference to `void barspace::DoSomething<__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > > >(__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >, __gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >)' 
collect2: ld returned 1 exit status 

Il y a un problème évident avec le code ne se fait disponible à partir du namespace ou de la bar unité de compilation.

De plus, lorsque je tente de placer la définition de DoSomething dans l'en-tête bar.h, comme je le ferais pour contourner les questions lors de la définition des méthodes de modèle de classe dans les fichiers .cpp séparés, je reçois la même erreur. Pouvez-vous jeter un peu de lumière sur mon erreur de liaison du compilateur?

Répondre

7

Vous essayez de cacher l'implémentation de votre fonction modèle dans le fichier cpp, ce qui, malheureusement, n'est pas possible pour la plupart des compilateurs. Les fonctions/classes modélisées sont instanciées lorsqu'elles sont utilisées, donc au point où vous appelez DoSomething, le compilateur a besoin de la définition de la fonction pour pouvoir le compiler.

Il existe plusieurs solutions.

  1. Déplacez le corps de la fonction dans le fichier d'en-tête. Vous aviez du mal à le faire avant, mais je dirais que c'est lié à autre chose. C'est l'approche préférée.

  2. Incluez le fichier cpp de foo.cpp. (sauvage, mais pas que peu commun).

  3. instancier le modèle pour double:

// bar.cpp 
#include "bar.h" 

namespace barspace { 

    template<> 
    void DoSomething<double> (double first, double last) { 
     typedef typename std::iterator_traits<double>::value_type val_t; 
     std::sort (first, last); 
    } 

} // namespace barspace 
1

Mettre des définitions de modèles dans un fichier source distinct n'est pas bien supporté (je crois que le seul compilateur qui supporte ce soit Comeau).

Vous devez déplacer la définition dans vos fichiers d'en-tête:

// bar.h 

namespace barspace { 

    template <typename Iter> 
    void DoSomething (Iter first, Iter last) { 
     typedef typename std::iterator_traits<Iter>::value_type val_t; 
     std::sort (first, last); 
    } 

} // namespace barspace 
1

De nombreux compilateurs n'aiment pas avoir des fonctions de modèle définies dans des fichiers séparés. Mettez toute la définition dans le fichier d'en-tête et elle devrait bien se compiler.

1

Vous devez définir les fonctions de modèle dans les en-têtes. Vous ne pouvez pas les déclarer, puis les définir dans un fichier d'implémentation; ça ne marchera pas. La raison en est que le compilateur doit instancier les modèles avant de pouvoir les appeler. Pour instancier un modèle de fonction du besoin compilateur deux choses:

  1. La définition du modèle
  2. Les paramètres du modèle à instancier avec

Dans votre cas, le compilateur fonctionne avec deux unités de compilation. Il peut très bien les exécuter dans deux processus différents, de sorte que les unités de compilation sont indépendantes les unes des autres. Lorsque le compilateur compile bar.cpp, il voit une définition de modèle sans demandes (appels) pour des instanciations spécifiques. Par conséquent, le compilateur n'instancie pas le modèle; compiler bar.cpp cède, en fait, rien. Le compilateur est assez "intelligent" pour voir que vous n'avez pas besoin de ce modèle, et l'optimise jusqu'à zéro.

Bien sûr, vous avez besoin de ce modèle - dans foo.cpp - mais c'est une unité de compilation différente, et maintenant le compilateur a tout oublié (ou n'a pas encore appris) à propos de bar.cpp. Le compilateur, cependant, voir une déclaration de la fonction de modèle, et il fait l'hypothèse habituelle que si elle a été déclarée alors il a été défini (instancié) ailleurs - et ne dit rien. Finalement, l'éditeur de liens arrive et obtient la vue ultime sur les oiseaux. Il peut voir qu'il n'y a pas DoSomething<Iter>(Iter, Iter) instancié pour std::vector<double>::iterator et se plaint.

Il existe quelques solutions à votre problème. La meilleure solution consiste à utiliser le mot clé export lorsque vous déclarez le modèle. Malheureusement, c'est aussi la pire solution, puisque la grande majorité des compilateurs ne supporte pas cette fonctionnalité standard. Mais sérieusement, votre meilleur pari est de définir votre modèle dans le fichier d'en-tête. Ne pas le déclarer là, définir là-bas. Vous n'aurez pas besoin de bar.cpp si c'est ce dont il s'agissait en premier lieu.

Questions connexes