link problem bei Templates



  • Hallo,

    Ich habe folgende Situation:

    2 Klassen, A und B sind gegeben. Klasse A enthält eine Liste von zeigern auf B. In B wiederum ist eine Liste von Zeigern auf C (hier int) gegeben. Mein Wunsch ist es jetzt, in A einen Iterator so zu definieren, dass über alle in allen B vorhanden C's iteriert werden kann.

    Hier erstmal die Klassen A, B

    class B
    {
    public:
      std::list<int*> t;
    };
    
    class A
    {
    public:
      std::list<B*> l;
    
      typedef doubleIterator<A, B, int, std::list<B*>, std::list<int*>> iter;
    
      iter _begin(void) const { return iter(this, l.begin()); }
      iter _end(void) const { return iter(this, l.end()); }
    };
    

    Mein Iterator funktioniert auch eigentlich einwandfrei, außer dass beim linken der Operator == nicht gefunden wird, wenn ich ihn nicht als Member-Funktion definiere. Hier einmal den Ausschnitt, der den Fehler noch enthält:

    #include <list>  
    
    template<class TA, class TB, class TC, 
      class Cont1 = std::list<TB*>, 
      class Cont2 = std::list<TC*> >
    class doubleIterator : public std::iterator_traits<typename Cont2::const_iterator>
    {
    public:
      typedef typename Cont1::const_iterator iterA;
      typedef typename Cont2::const_iterator iterB;
    
      inline doubleIterator(const TA* a, iterA ia) {}
    
    private:
      const TA* _a;
      iterA _ia;
      iterB _ib;
      bool bInit;
    
    public:
      friend bool operator==(const doubleIterator& a, const doubleIterator& b);
    };
    
    template<class TA, class TB, class TC, class Cont1, class Cont2>
    bool 
    operator==(const doubleIterator<TA, TB, TC, Cont1, Cont2>& a,
               const doubleIterator<TA, TB, TC, Cont1, Cont2>& b)
    {
      return true; //hier steht was länglicheres ;)
    }
    

    Beim Kompilieren von folgendem Testprogramm

    int main(int, char**)
    {
      A a;
    
      A::iter i = a._begin();
    
      if (i == a._end())
      {
      }
    }
    

    bekomm ich folgenden Fehler:

    1>main.obj : error LNK2019: unresolved external symbol "bool __cdecl operator==(class doubleIterator<class A,class B,int,class std::list<class B *,class std::allocator<class B *> >,class std::list<int *,class std::allocator<int *> > > const &,class doubleIterator<class A,class B,int,class std::list<class B *,class std::allocator<class B *> >,class std::list<int *,class std::allocator<int *> > > const &)" (??8@YA_NABV?doubleIterator@VA@@VB@@HV?doubleIterator@VA@@VB@@HV?list@PAVB@@V?allocator@PAVB@@@std@@@std@@V?allocator@PAVB@@@std@@@std@@V?list@PAHV?$allocator@PAH@std@@@4@@@0@Z) referenced in function _main

    Ich benutze Visual Studio 2005 (Professional) - Sollte das irgendwie zur Problemlösung helfen.

    Ich habe schon versucht, mit

    template<> bool operator==<A, B, int, std::list<B*>, std::list<int*> >(const doubleIterator<A, B, int, std::list<B*>, std::list<int*> >& a,
               const doubleIterator<A, B, int, std::list<B*>, std::list<int*> >& b);
    

    eine Instantierung des Templates zu erzwingen, jedoch führte dies leider auch nicht zum Erfolg.

    Alle MemberFunktionen werden korrekt instantiiert, sprich, wenn ich operator== als Member-Funktion machen, funktiniert alles ... Jedoch hätte ich es gerne als externe Funktion.

    Vielen Dank für Hilfe schonmal im vor raus. (Wieso wird vor raus (zusammengeschrieben) zensiert ...)

    mfg Thorondir



  • EDIT: Ich nehm alles zurück. War falsch, was ich geschrieben hab...



  • In dem Beispiel-programm oben steht alles in einer einzelnen Datei drin - daran sollte es nicht liegen - Macht ja jetzt keinen Unterschied, ob ich den kram in eine header datei schreibe und dann einbinde oder direkt in die (gleiche) Cpp-Datei schreibe.

    @voraus: Falsch geschrieben ist es, aber es sollte deswegen trozdem nicht zensiert werden 😉



  • Ich habe noch was rumprobiert: Es scheint mit dem friend zusammenzuhängen, denn:

    #include <list>  
    
    template<class TC, class Cont1>
    class doubleIterator 
    {
    public:
      /* auskommentieren, und alles klappt ... */
      friend bool foo(const doubleIterator& a, const doubleIterator& b);
    };
    
    template<class TC, class Cont1>
    bool foo(const doubleIterator<TC, Cont1>& a, const doubleIterator<TC, Cont1>& b)
    {
      return true;
    }
    
    class A
    {
    public:
      std::list<int*> l;
    
      typedef doubleIterator<int, std::list<int*> > iter;
    
      iter _end(void) const { return iter(); }
    };
    
    int main(int, char**)
    {
      A a;
    
      if (foo(a._end(), a._end()))
      {
      }
    
    }
    

    Wenn ich das friend rausnehme, kompiliert das Beispiel, wenns drin ist, findet er die Funktion nicht ...

    Ah, beim weiterherum probieren beim schreiben hats dann auch funktioniert:

    template<class TC, class Cont1>
    class doubleIterator 
    {
    public:
      /* auskommentieren, und alles klappt ... */
      template<class _TC, class _Cont1>
      friend bool foo(const doubleIterator<_TC, _Cont1>& a, const doubleIterator<_TC, _Cont1>& b);
    };
    

    Damit kompiliert es, für alle die eine Lösung haben wollen ...



  • was genau kann dein doubleIterator denn eigtl? Oo
    In deinem Quellcode hast du ja (sinnigerweise) alles unnötige rausgelassen - aber was genau sprach hier denn gegen einen normalen Iterator?
    (und wenn wir schon mal dabei sind: wieso eine list<int*>? mir fällt hier genau ein vorteil auf: die iteratoren bleiben gültig, auch wenn du drin rum löschst/verschiebst - ansonsten sollte es aber um ein vielfaches langsamer sein...)

    bb



  • int* ist nur zum Testen.

    Eigentlich sind in der Liste Zeiger auf einen anderen Datentyp drin.

    Das sieht ungefähr halt so aus:

    class C
    {
    };
    
    class B
    {
    std::list<C*> l;
    };
    
    class A
    {
    std::list<B*> l;
    typedef ... iterator;
    iterator begin() ...
    iterator end() ...
    }
    

    Der Iterator soll dabei dann halt über alle Elemente C iterieren, die halt auf die Klassen B aufgeteilt sind. Die Klassen B sind halt notwendig und versorgen jeweils eine bestimmte Anzahl von C, jedoch ist es ganz nett, wenn man sich alle C, die irgendwie über ein B in A verlinkt sind, auf einmal (mit einem Iterator) ansehen kann - und das kann der doubleIterator.

    list ist auch nicht unbedingt benötigt, im Endeffekt kann der, so wie ich ihn programmiert habe, auf alle Container angewandt werden, die Iteratoren besitzen -- nur das ist halt etwas unübersichtlich wegen der ganzen dafür benötigten template parameter:

    template<class TA, class TB, class TC, // Die 3 Klassen A, B und C
      class Cont1 = std::list<TB*>, // Container 1 in Klasse A mit Zeigern auf B
      class Cont2 = std::list<TC*>, // Container 2 in Klasse B mit Zeigern auf C
        typename Cont1::const_iterator (TA::*faEnd) (void) const = &TA::end, // gibt das Ende von Container 1 zurück
        typename Cont2::const_iterator (TB::*fbBegin)(void) const = &TB::begin, // gibt den Anfang von Container 2 für ein Element zurück
        typename Cont2::const_iterator (TB::*fbEnd)(void) const = &TB::end> // gibt das Ende von Container 2 zurück
      class doubleIterator : public std::iterator_traits<typename Cont2::const_iterator>
    

Anmelden zum Antworten