Toon posts:

[C++]template: List en operator<<

Pagina: 1
Acties:

Verwijderd

Topicstarter
Een gelinkte lijst (een template) kan die eigenlijk een operator<< hebben? Want het lukt mij niet de operator<< te implementeren voor een template gelinkte lijst. Zodat ik dus een lijst via de << operator kan afprinten.

Mijn lijst is van de vorm:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
template <class T>
class Lijst {
    private:
        struct knoop {
            T waarde;
            knoop* next;
            knoop(T x) {waarde=x; next=NULL;}
        };
        knoop* head;

    public:
/*KNIP*/
                friend std::ostream& operator << (std::ostream& os,Lijst<T> &l);

Indien ik dan de operator<< zelf wil schrijven en gebruiken dan kan ik onmogelijk daarin een knoop* enzo declareren om de lijst te overlopen.
Maar een friend die kan toch gewoon aan de private variabelen? Nu denk ik dat dit komt doordat die struct knoop niet door de friend kan gebruikt worden, echter hoe los ik dit dan proper op?

  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 99% gewijzigd door Eelis op 18-02-2015 19:16 ]


  • mandroid
  • Registratie: Juli 2002
  • Laatst online: 01-02 07:50
je kan die struct definitie toch altijd uit je klasse halen ? (die ik persoonlijk altijd)

ik heb het even geprobeerd met de struct definitie erin en dit lukt bij mij :
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

template <class T> 
class Lijst { 
    private: 
        struct knoop { 
            T waarde; 
            knoop* next; 
            knoop(T x) {waarde=x; next=NULL;} 
        }; 
        knoop* head; 

    public: 
    friend std::ostream& operator << (std::ostream& os,Lijst<T> &l);
};

template <class T>
std::ostream& operator<<(std::ostream& os,Lijst<T> &l){
    knoop* temp = l.head;
    while(temp->next != 0){     //afhankelijk van implementatie
        std::cout << temp->waarde << std::endl;
        temp = temp->next;
    }
}

  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 99% gewijzigd door Eelis op 18-02-2015 19:16 ]


  • mandroid
  • Registratie: Juli 2002
  • Laatst online: 01-02 07:50
Eelis schreef op 30 juli 2004 @ 21:45:
[...]

Welke compiler gebruik je? Noch GCC (3.4.1) noch Comeau accepteren deze code.
VS.Net

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

The joys of template friends...lees dit eens dit dus..

En dan vervolgens zijn er een aantal compilers die buggen, en een aantal die standard compliant zijn en dan heb je nog problemen met koenig lookup gerelateerde friend namespace injection. Wat er op neer komt dat je je friend functie wel defineert in de class, maar niet daar buiten en dat alsnog een keer moet doen.

Het komt er dus op neer dat je compiler die template friend niet ziet als template functie, maar als normale functie met argumenten afhankelijk van de template instantie. En die zijn nergens gedefineerd, alleen maar een template functie.

[ Voor 24% gewijzigd door Zoijar op 30-07-2004 22:35 ]


Verwijderd

Topicstarter
Bedankt voor de tips maar...
Eelis schreef op 30 juli 2004 @ 21:45:
[...]
Welke compiler gebruik je? Noch GCC (3.4.1) noch Comeau accepteren deze code.
En onder mijn compiler werkt het ook niet (VC++ 6.0) ik had ongeveer dezelfde code overigens die dus niet werkte.
Deze dus:
C++:
1
2
3
4
5
6
7
8
9
template <class T>
std::ostream& operator << (std::ostream& os,const Lijst<T> &l) {
    knoop* temp = l.begin;
    while(temp->next!=NULL){
        os << temp->waarde << std::endl;
        temp = temp->next;
    }
     return os;
}


De tips van Zoijar hebben jammergenoeg ook niet geholpen, ik was me overigens bewust van die bug, echter ik wist dat die bug niet meer zat op mijn compiler (na het installeren van de nodige SP).
Ik heb het overigens uit zekerheid toch es gepredefinieerd maar zoals gedacht hielp dat dus niet.

de errors:
f:\temp\linked_list.h(141) : error C2065: 'knoop' : undeclared identifier
f:\temp\test_linked_lijst.cpp(34) : see reference to function template instantiation 'class std::basic_ostream<char,struct std::char_traits<char> > &__cdecl operator <<(class std::basic_ostream<char,struct std::char_traits<char> > &,const cl
ass Lijst<int> &)' being compiled
Jah en doordat de compiler die knoop temp niet kan declareren zijn er uiteraard ook nog errors alla temp undeclared identifier enz...
m.a.w. die private members kan die friend functie blijkbaar niet zien :?

[ Voor 6% gewijzigd door Verwijderd op 31-07-2004 08:30 ]


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Dit werkt gewoon hoor onder VC7.1

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>

template <class T> class Lijst;
template <typename T> std::ostream& operator<< (std::ostream& os, Lijst<T> &l);

template <class T> 
class Lijst { 
    private: 
        struct knoop { 
            T waarde; 
            knoop* next; 
            knoop(T x) {waarde=x; next=NULL;} 
        }; 
        
        knoop* head; 

    public: 

    friend std::ostream& operator<< <> (std::ostream& os, Lijst<T> &l);
};

template <class T>
std::ostream& operator<<(std::ostream& os, Lijst<T> &l){
    Lijst<T>::knoop* temp = l.head;
    return os;
}
                
int main(int argc, char* argv[]) {
    typedef Lijst<int> ILijst;

    ILijst list;
    std::cout << list;
}


Oh wacht je gebruikt VC6, ja die bugged inderdaad met friend functies. Heb ik laatst nog iets omheen gebouwd, zoek het even op...Je gebruikt geen 'using namespace std;' toch? oh met service packs schijnt dat allemaal gefixed te zijn.

Maar je gebruikt ook geen scope resolution bij die knoop* zie ik net, dat is het dan.

[ Voor 49% gewijzigd door Zoijar op 31-07-2004 11:10 ]


  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 99% gewijzigd door Eelis op 18-02-2015 19:16 ]


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Eelis schreef op 31 juli 2004 @ 11:53:
[...]
  • Volgens gcc ontbreekt "typename" op regel 24 in jouw code.
  • Als je de friend declaratie op regel 19 vervangt door:
    C++:
    1
    2
    
    template <typename U>
    friend std::ostream& operator<< (std::ostream& os, Lijst<U> &l);
    heb je die forward declaraties niet nodig.
Dat typename klopt ja, vergeet ik vaak omdat vc7 er niet over klaagt. Maar het moet idd wel ja...

Het is wel handig om forward te declaren, omdat die friend declaration in je class niet in je omliggende namespace injected wordt. Als je dus de operator<< gebruikt voor de definitie van die functie krijg je anders fouten.

  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 99% gewijzigd door Eelis op 18-02-2015 19:17 ]


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Stel dat die template operator<< definitie pas later komt dan de operator<< gebruikt wordt. (dit kan als je verschillende headers include in bepaalde volgordes) De friend declaratie wordt niet geinjecteerd in de omliggende namespace, en is daar dus niet bekend. Nou is het punt hier dat het wel werkt, dat er door het lijst argument via koenig lookup ook in Lijst wordt gezocht. De operator wordt dan goed gevonden, geen probleem.
Maar als je bv een andere type hebt, dat gecast kan worden naar een lijst, dan kijkt de koenig lookup bij dat andere type en zal de operator<< niet gevonden worden.

Een beetje vergezocht (het is zelfs lastig om hier een voorbeeld van in elkaar te prutsen met deze code), toegegeven, maar het zou wel plotseling een hele vage error kunnen geven.

edit:
Het zou kunnen dat dit al weer veranderd is, er wordt namelijk druk gediscussieerd over namespace injection. Ik zie ook dat Comeau zelfs een eigen implementatie heeft gekozen omdat de standaard niet duidelijk is. Dat wil wel wat zeggen...

[ Voor 16% gewijzigd door Zoijar op 31-07-2004 12:44 ]


Verwijderd

Topicstarter
Hmmm ik snap het toch nog niet zo goed ondanks dat ik nu het volgende heb:
C++:
1
2
3
4
5
6
7
8
9
template <class T> 
std::ostream& operator<<(std::ostream& os, Lijst<T> &l){ 
    Lijst<T>::knoop* temp = l.begin;
    while(temp->next!=NULL){
        os << temp->waarde << std::endl;
        temp = temp->next;
    }
    return os;
}

die Lijst<T>:: was ik daar vergeten constateer ik toch nog het volgende:

Indien ik net zoals Zoijar alles implementeer in één file dan is er geen enkel probleem (dus de main samen met heel de linked list implementatie).
Echter indien ik de main in een apparte cpp file zet en dus een linked list implementatie heb in één h-file dan krijg ik nog steeds de volgende twee errors:
f:\temp\linked_list.h(143) : error C2248: 'knoop' : cannot access private struct declared in class 'Lijst<int>'
f:\temp\linked_list.h(16) : see declaration of 'knoop'
F:\Temp\test_linked_lijst.cpp(34) : see reference to function template instantiation 'class std::basic_ostream<char,struct std::char_traits<char> > &__cdecl operator <<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Li
jst<int> &)' being compiled
f:\temp\linked_list.h(143) : error C2248: 'begin' : cannot access private member declared in class 'Lijst<int>'
f:\temp\linked_list.h(22) : see declaration of 'begin'
F:\Temp\test_linked_lijst.cpp(34) : see reference to function template instantiation 'class std::basic_ostream<char,struct std::char_traits<char> > &__cdecl operator <<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Li
jst<int> &)' being compiled
Ik heb de volledige source tijdelijk online gezet op http://users.pandora.be/wyffels.f/allerlei/lijsten.zip

Opmerkingen zijn altijd welkom, in de source de test2.cpp file die werkt volledig (hier toch) terwijl de twee andere files (de losse h-file en de cpp file) niet werken.

't zal wel weer iets stoms zijn dat ik niet zie. Bedankt voor de reeds interessante opmerkingen.

De fout(en) kunnen dan gequote worden dan heeft men er later ook nog iets aan.

[ Voor 41% gewijzigd door Verwijderd op 31-07-2004 14:15 ]


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Er moet wel typename voor die Lijst<T>:: zoals Eelis al zei. Dus:

C++:
1
typename Lijst<T>::knoop


Maar dat terzijde, het ziet eruit als VC6 bug. Ik weet dat het origineel grote problemen heeft met dit soort dingen (helemaal als er ook nog namespaces bij komen kijken) Wat de verschillende SPs daar van gefixed hebben weet ik niet. Ik zal zo ff naar je code kijken als ik tijd heb, het kan ook dat er nog ergens een declaratie mist oid.

  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 107% gewijzigd door Eelis op 18-02-2015 19:17 ]


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

VC7.1 compiled het goed hier, mits je idd die in-class friend declaratie aanpast zoals hier gezegd. Ik zie dat je 'using namespace std;' gebruikt, heb je hier naar gekeken?

MS KB

Maw, probeer het eens met die pre-declarations. Dus dat (niet nodig) stukje uncommenten. Het komt gedeeltelijk omdat VC6 geen volledige koenig lookup etc ondersteunt, maar een soort van eigen special case code voor operator heeft omdat dat wel vaak voorkwam. En blijkbaar als je dan ook nog een default namespace binnen haalt, dan gaat het mis. Daar heeft het iets mee te maken...Dus zo veel mogelijk declararen kan geen kwaad, een standard compliant compiler heeft namelijk verder geen moeite met wat extra declaraties van hetzelfde.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template <class T> class Lijst; 
template <typename T> std::ostream& operator<< (std::ostream& os,const Lijst<T> &l);

template <class T>  
class Lijst {  
 
// blah blah blah ...

    template <typename U>
        friend std::ostream& operator<< (std::ostream& os, Lijst<U> &l);
};

template <class T> 
std::ostream& operator<<(std::ostream& os, Lijst<T> &l){ 
    typename  Lijst<T>::knoop* temp = l.begin;

// blah blah ...
}

[ Voor 75% gewijzigd door Zoijar op 31-07-2004 14:45 ]


Verwijderd

Topicstarter
Eelis schreef op 31 juli 2004 @ 14:32:
Je kunt er trouwens ook voor kiezen om deze friend-problemen (die wellicht deels veroorzaakt worden door de gebrekkige standard-conformance van VC6) te omzeilen door Lijst een publieke ostream & print (ostream &) const member te geven die je dan vervolgens vanuit de operator<< aanroept.
Dat weet ik maar dat was niet mijn bedoeling om te doen, toch bedankt voor de tips.

Zoijar ook bedankt al wil het hier nog altijd niet werken ondanks de aanpassingen. (ik heb de source aangepast en op het zelfde adres online gezet)

Dus volgens jullie zou het dan zoals het er nu opstaat goed moeten compileren normaal gezien (dus zonder de bugs in VC++ 6.0)?

ik zal ook es kijken straks of het met gcc goed compileerd.

[ Voor 20% gewijzigd door Verwijderd op 31-07-2004 15:40 ]


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Hmm ja werkt prima hier met vc7.1...zelfs met /W4. Misschien ipv 'using namespace std;' veranderen in 'using std::cout; using std::endl;' dat is sowieso beter, maar betwijfel of het iets oplost. Ik heb hier geen vc6 (durf ik ook niet te installeren naast 7) dus kan verder niet testen.

Herinner me nu trouwens dat ik ooit op school ook een linked list in VC6 heb geschreven, en daar heb ik toen als oplossing de friend in de class gedefineerd. (met comment dat het een compiler bug is) Dat werkte wel. Dus gewoon dit in je Lijst class:

C++:
1
2
3
4
5
6
7
8
        friend std::ostream& operator<< (std::ostream& os, Lijst<T> &l) {
            typename Lijst<T>::knoop* temp = l.begin;   //typename is nodig
            while(temp->next!=NULL){
                os << temp->waarde << std::endl;
                temp = temp->next;
            }
            return os;
        }


Geen schoonheidsprijs idd, maarja, je moet toch iets.

edit:
(oh ik heb steeds alleen maar naar test_linked_lijst.cpp gegeken, ik dacht dat test2.cpp oud was)

[ Voor 72% gewijzigd door Zoijar op 31-07-2004 16:16 ]


  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 99% gewijzigd door Eelis op 18-02-2015 19:18 ]


Verwijderd

Topicstarter
Zoijar schreef op 31 juli 2004 @ 16:05:
Hmm ja werkt prima hier met vc7.1...zelfs met /W4. Misschien ipv 'using namespace std;' veranderen in 'using std::cout; using std::endl;' dat is sowieso beter, maar betwijfel of het iets oplost. Ik heb hier geen vc6 (durf ik ook niet te installeren naast 7) dus kan verder niet testen.

Herinner me nu trouwens dat ik ooit op school ook een linked list in VC6 heb geschreven, en daar heb ik toen als oplossing de friend in de class gedefineerd. (met comment dat het een compiler bug is) Dat werkte wel. Dus gewoon dit in je Lijst class:

C++:
1
2
3
4
5
6
7
8
        friend std::ostream& operator<< (std::ostream& os, Lijst<T> &l) {
            typename Lijst<T>::knoop* temp = l.begin;   //typename is nodig
            while(temp->next!=NULL){
                os << temp->waarde << std::endl;
                temp = temp->next;
            }
            return os;
        }


Geen schoonheidsprijs idd, maarja, je moet toch iets.

edit:
(oh ik heb steeds alleen maar naar test_linked_lijst.cpp gegeken, ik dacht dat test2.cpp oud was)
Geeft een vage LINK error, nuja speelt geen rol ik weet nu dat de code goed is, zal het straks in gcc compileren en dan is't ook in orde.

bedankt voor de tips

Ohja die test2.cpp jah die speelt niet zoveel rol want die zit al lang in de prullenbak >:)

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Om het even te bevestigen: VC6 heeft een zwaar buggy template implementatie. Templates in templates werken niet zo. Koenig lookup is beperkt, en afwezig voor non-operators. Service packs kunnen dit niet fixen, te complex.

Overigens heeft VC6 een std::list< > met een indrukwekkende hoeveelheid workarounds voor VC6 bugs.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein

Pagina: 1