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?list@PAVB@@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>