Iterator begin und end falsch



  • Hallo SeppJ,

    erst mal riesigen Dank für deine ausführlichen Antworten! Super! 🙂

    Den Fehler in der main habe ich selber entdeckt und gefixt. Der war sehr dämlich.

    Zu deinen Kommentaren:

    Indem du es richtig machst.
    Hast du mal nachgeschlagen, was Pointerarithmetik überhaupt ist? Dir scheint gar nicht bewusst zu sein, dass es das gibt. Im Prinzip heißt Pointerarithmetik, dass du gar nicht machen brauchst, weil alles automatisch richtig passiert.

    Ja habe ich, in den alten C Tagen. 😉 Seit dem habe ich es vermieden. Ich dachte eigentlich, da gibt es was cooles C++ mäßiges, wie static_cast<int>(x) statt (int)x, was ich nicht kenne. 😉 Besser überlegen sollte man da auf jeden Fall und in der aktuellen Variante komme ich ja auch erst mal ohne aus, auch wenn ich mir dadurch nen Problem mit dem const_iterator einhandle. 😞

    Zu den Anmerkungen:

    -Operator<<: Warum der Stringstream?

    Der Code ist aus meinen richtigen Programm rausgenommen, wo der Output entweder auf den outstream geht oder auf so nen komisches DEBUG macro. Per stringstream war es einfacher beide Fälle zu behandeln, ich habs aber nun zusammengekürzt.

    -Deine Default- und Kopierkonstruktoren, Zuweisungsoperatoren und Destruktoren machen effektiv alle das gleiche wie die automatisch generierten.

    Ich dachte bisher, dass man selbst in diesen Fällen nach der Regel der goldenen 3 (jetzt wohl 5) diese immer angeben sollte, selbst wenn die automatisch generierten reichen würden. Ist dann wohl nen irrglaube.

    -Komische Semantik von add. Klingt eher wie insert oder push_back.

    Nun, in der Tat ist das hier wohl ne Ansichtssache. Ich persönlich finde, das ein insert ein konkretes einfügen an einer Stelle ist. Somit kann semantisch für mich ein insert immer nur sagen: füge x zwischen y und z ein. Ein add fügt hingegen einfach nur etwas einer Menge hinzu. Da ne Liste erst einmal nichts weiter als eine Menge ist, passt aus meinen Augen besser add statt insert. Aber dies ist wohl Geschmackssachen. Ein push_back kommt allerdings nicht in Frage, da es auf eine konkrete Implementierung verweißt, und die List-Klasse auf verschiedenen Plattformen immer gleich sein soll. Mal im Simulator, mal in der echten Welt, unabhängig ob es nun intern ein vector, nen array oder ne QList ist. Push_back würde somit dem Nutzer sagen, hey, ich bin ein vector, was unter Umständen aber gar nicht so ist.

    Wie schon erwähnt, die Klasse mutiert seit mehren Generationen. Nun will ich es besser machen. Langfristig würde ich auch gern den vector gegen was schöneres austauschen (schon wegen der Semantik), allerdings wird zumeist die list von 0..x durchlaufen, was ja ne Stärke des vectors ist. was anderes habe ich nicht gefunden.

    -add(const T* tArray, size_t length) -> Iteratoren statt Zeiger + Länge. Macht das gleiche, ist aber viel flexibler.

    Ausgetauscht. Cooler Tipp. Danke.

    -throw Integertyp?

    Nur wegen der Demo, sonst wird da ne Subklasse von exception geworfen, mit ner Fehlermeldung.

    -bounce = prellen. bounds = Grenzen

    Peinlich, gefixt.

    -komische Semantik von remove. Mal entfernt es bestimmte Elemente, mal alles mit einem bestimmten Wert.

    Nun, die List speichert im allgemeinen Instanzen eines Typs. Zum Beispiel Autos. Das remove so wie hier verwendet, soll dann auch ermöglichen, dass ich das Auto löschen kann, wenn ich nur die Fahrgestellnummer kenne und nicht das Auto an sich habe. In der Extraktion fürs Minibeispiel kommt dann sowas komisches raus.

    -sort: Wenn dein sort nichts spezielles leistet, wieso dann ein sort anbieten?

    Es wird oft verwendet und spart somit Codezeilen. Als es eingeführt wurde, war das sortieren wesentlich umständlicher als heute. Ist somit ne Art legacy code, von dem ich mich noch nicht trennen kann. Clean code mäßig von ich es lesbaren zu sagen liste sortiere dich, statt die liste selbst zu sortieren als Nutzer.

    -komische Semantik des Vergleichsoperators. Überall sonst ist bei dir die Reihenfolge wichtig, hier auf einmal nicht mehr.

    Den versteh ich nicht. Was ist da falsch?

    Aktuell habe ich versucht den Iterator einfach "durchzuschleifen" um somit das Pointer-Thema loszuwerden. Leider laufe ich somit in das Problem, dass ich nen const T im vector speichern will, was nicht erlaubt ist. Nun ist die Frage, wie kann ich einen const_iterator definieren???

    Die aktuelle Version ist hier zu finden:
    http://codepad.org/5bz7rNYy

    Beim googlen habe ich entweder nur Varianten gefunden, welche den iterator als typedef von vector::iterator definieren, oder für komplett eigene Implementierungen eine schreiben. Solche wrapper-Iteratoren habe ich noch nicht gefunden. Den vector möchte ich aber nicht rausgeben, da sonst viele den vector direkt verwenden könnten und ich dann Probleme bekomme, wenn ich den vector gegen was schöneres austausche.

    Verständlich?



  • hallo nochmal,

    ich habe mein const_iterator nun so gefixt:

    class List {
    public:
    
        template <typename L>
        class Iterator : public std::iterator<std::forward_iterator_tag, L> {
    
            typename std::vector<typename std::remove_const<L>::type>::iterator index;
    
            friend class List<T>;
    
            explicit Iterator(typename std::vector<typename std::remove_const<L>::type>::iterator it);
    
        public:
    
            Iterator();
    
            Iterator(Iterator<typename std::remove_const<L>::type > const & other);
    
            L & operator*();
    
            Iterator<L> & operator++(); // prefix
            Iterator<L> operator++(int); //postfix
    
            bool operator !=(const Iterator<L> & other) const;
            bool operator ==(const Iterator<L> & other) const;
    
            Iterator<L> operator=(const Iterator<L> & other);
            Iterator<L> operator+(const unsigned int position);
            Iterator<L> operator-(const unsigned int position);
    
        };
    
        typedef Iterator<T> iterator;
        typedef Iterator<const T> const_iterator;
    

    Kann man dies so lassen, oder sind da gröbere Fehler drin? (Ich weiß, ist nur die Signatur, aber um Platz zu sparen. 🙂 )


  • Mod

    Kann man dies so lassen, oder sind da gröbere Fehler drin?

    Warum kann man einem const_iterator keinen iterator zuweisen? (Edit: Oder geht das etwa durch den Konvertierungskonstruktor?)

    Übrigens musst du innerhalb der Definition des Klassentemplates Iterator nicht mit der Argumentenliste qualifizieren, da es der injected-class-name ist - also bspw.

    Iterator<L> operator=(const Iterator<L> & other);
    // =>
    Iterator operator=(const Iterator & other);
    


  • wo meinst? übersehe ich was?

    einen const_iterator auf nen iterator sollte doch eh nicht gehen, da ihm doch die "schreibrechte" fehlen. anders herum müßte es gehen, da man dem iterator ja rechte entzieht.


  • Mod

    Doch, funktioniert, durch den Konvertierungskonstruktor, hab's mir schon gedacht. Dabei muss aber eine Temporary erstellt werden - eigentlich übeflüssig.

    Vielleicht solltest du den Zuweisungsoperator auch umschreiben:

    #include <iostream>
    #include <iterator>
    
    template <typename L>
    class Iterator : public std::iterator<std::forward_iterator_tag, L>
    {
    public:
    
    	Iterator(){}
    
    	Iterator(Iterator<typename std::remove_const<L>::type > const & other) { std::cout << "Konvertierungs-Ctor"; }
    
    	Iterator<L> operator=(const Iterator<L> & other) { return *this; }
    };
    
    typedef Iterator<int> iterator;
    typedef Iterator<const int> const_iterator;
    
    int main()
    {
    	iterator i;
    	const_iterator i2;
    	i2 = i;
    }
    


  • wie gehts den besser?



  • sven_ schrieb:

    einen const_iterator auf nen iterator sollte doch eh nicht gehen, da ihm doch die "schreibrechte" fehlen. anders herum müßte es gehen, da man dem iterator ja rechte entzieht.

    Arcoth hat es ja auch anders herum geschrieben.
    Du musst auf die Fälle achtgeben:
    einem A einen B zuweisen
    !=
    einen A einem B zuweisen



  • Sorry jungs,

    ich raffe es einfach nicht. Wenn ich den operator<< nutzen will, fliegt mir der const_iterator um die Ohren. Wie muß das Scheißding den nun richtig aussehen?????? Ich check es einfach nicht.



  • hallo hustbaer,

    vielleicht sitz ich ja auf der leitung, aber ich checke es einfach nicht. 😞 😕


  • Mod

    sven_ schrieb:

    vielleicht sitz ich ja auf der leitung, aber ich checke es einfach nicht. 😞 😕

    Er und ich meinen:

    Einem A einen B zuweisen: A = B
    Einen A einem B zuweisen: B = A

    Und du solltest einen Zuweisungsoperator bereitstellen der es erlaubt einem const_iterator einen iterator zuzuweisen, um die Temporary zu vermeiden.



  • na salop gesagt, wen interessiert aktuell das temporary. ich schaffe es doch offensichtlich noch nicht mal korrekt nen const_iterator zu erstellen, da:

    std::cerr<<tempList<<"\n";

    massive auf die schnauze fällt. oder hängt dies damit auch zusammen.

    ich versuche mal den zuweisungsoperator zu basteln.



  • also ich bekomms leider echt nicht hin. laut meiner logik (welche bestimmt falsch ist), muß ich doch nun zwei zuweisungsoperatoren anbieten:

    operator=(const_it ...)
    operator=(it ...)

    das wäre dann, also:

    Iterator< <typename std::remove_const<L>::type > > operator=(const Iterator<typename std::remove_const<L>::type > & other);
    
    Iterator<const <typename std::remove_const<L>::type > > operator=(const Iterator<const typename std::remove_const<L>::type > & other);
    

    Der compiler sagt da aber:

    ISO C++ forbids declaration of ‘type name’ with no type [-fpermissive]
    List.h:55:71: error: template argument 1 is invalid
    

    Im Prinzip müßte es aufgrund des Templates eher außerhalb von Iterator definiert werden, da ist es aber nicht erlaubt. Also wie soll das gehen? 😕

    😞



  • Kein Wunder, wenn die Syntax falsch ist.

    Iterator< <typename std::remove_const<L>::type > >
    Iterator<const <typename std::remove_const<L>::type > >
    

    Was sollen zwei öffnende < bezwecken?



  • stimmt, passiert wohl, wenn man nicht mehr durchsieht. 😞

    War es mit den beiden zuweisungsoperatoren so gemeint:

    Iterator operator=(Iterator<typename std::remove_const<L>::type > & other) {
    
                if (this != &other) {
                    std::iterator<std::forward_iterator_tag, L>::operator=(other);
                    index = other.index;
                }
    
                return *this;
            }
    
            Iterator operator=(Iterator< const typename std::remove_const<L>::type > & other){
    
                if (this != &other) {
                    std::iterator<std::forward_iterator_tag, L>::operator=(other);
                    index = other.index;
                }
    
                return *this;
            }
    

    ???

    Der aktuelle Stand meiner Versuche wäre dann dieser:
    http://codepad.org/UaaoEnDp

    Was hierzu führt:

    main.cc: In instantiation of ‘List<T>::const_iterator List<T>::begin() const [with T = int; List<T>::const_iterator = List<int>::Iterator<const int>]’:
    main.cc:226:27:   required from ‘std::ostream& operator<<(std::ostream&, const List<T>&) [with T = int; std::ostream = std::basic_ostream<char>]’
    main.cc:454:16:   required from here
    main.cc:210:41: error: no matching function for call to ‘List<int>::Iterator<int>::Iterator(const TList&, std::vector<int, std::allocator<int> >::const_iterator)’
    main.cc:210:41: note: candidates are:
    main.cc:124:1: note: List<T>::Iterator<L>::Iterator(const List<T>::Iterator<typename std::remove_const<L>::type>&) [with L = int; T = int; typename std::remove_const<L>::type = int]
    main.cc:124:1: note:   candidate expects 1 argument, 2 provided
    main.cc:119:1: note: List<T>::Iterator<L>::Iterator(typename std::vector<typename std::remove_const<L>::type>::iterator) [with L = int; T = int; typename std::vector<typename std::remove_const<L>::type>::iterator = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >]
    main.cc:119:1: note:   candidate expects 1 argument, 2 provided
    

  • Mod

    Zeig doch mal Zeile 210. Du versuchst anscheinend einen Iterator mit zwei Argumenten zu initialisieren (einer TList und einem const_iterator), aber dein Iterator kennt nur zwei Konstruktoren, von denen beide jeweils einen Parameter von einem Iteratortyp erwarten.



  • Hallo zusammen,

    da ich gestern durch das Spiel ein wenig abgelenkt war erst heute hier die Antwort. 🙂

    template <typename T>
    std::ostream&
    operator<<(std::ostream & out, const List<T> & l) {
    
        out << "[";
    
        typename List<T>::const_iterator e;
        for ( e = l.begin(); e != l.end(); e++) {
            out << (*e);
        }
        out << "]";
        return out;
    }
    

    Die Zeile 217 ist die Zeile mit den for-Schleifen-Kopf:

    main.cc: In instantiation of ‘std::ostream& operator<<(std::ostream&, const List<T>&) [with T = int; std::ostream = std::basic_ostream<char>]’:
    main.cc:458:18:   required from here
    main.cc:217:24: error: no match for ‘operator=’ in ‘e = (& l)->List<T>::begin<int>()’
    main.cc:217:24: note: candidates are:
    main.cc:143:1: note: List<T>::Iterator<L> List<T>::Iterator<L>::operator=(List<T>::Iterator<typename std::remove_const<L>::type>&) [with L = const int; T = int; typename std::remove_const<L>::type = int]
    main.cc:143:1: note:   no known conversion for argument 1 from ‘List<int>::const_iterator {aka List<int>::Iterator<const int>}’ to ‘List<int>::Iterator<int>&’
    main.cc:156:1: note: List<T>::Iterator<L> List<T>::Iterator<L>::operator=(List<T>::Iterator<const typename std::remove_const<L>::type>&) [with L = const int; T = int; typename std::remove_const<L>::type = int]
    main.cc:156:1: note:   no known conversion for argument 1 from ‘List<int>::const_iterator {aka List<int>::Iterator<const int>}’ to ‘List<int>::Iterator<const int>&’
    

    Die Konstruktoren sind:

    template <typename T>
    template <typename L>
    List<T>::Iterator<L>::Iterator(){
    }
    
    template <typename T>
    template <typename L>
    List<T>::Iterator<L>::Iterator(typename std::vector<typename std::remove_const<L>::type>::iterator it) : index(it) {
    }
    
    template <typename T>
    template <typename L>
    List<T>::Iterator<L>::Iterator(Iterator< typename std::remove_const<L>::type > const & other) : index(other.index) {
    }
    

    Der vollständige Code steht hier: http://codepad.org/SwPgqGmi


  • Mod

    operator=(List<T>::Iterator<typename std::remove_const<L>::type>&)
    

    Faellt dir nichts auf an dieser Zeile? Fehlt fa nicht irgendwo ein const ?



  • Ok, da war noch ein wenig alter Schrott drin, und das const fehlte in der Tat. Ein Schritt weiter, hoffe ich.

    Aktueller Stand: http://codepad.org/8GnALiZN

    So wie ich es sehe, ist das Problem hier:

    template <typename T>
    typename List<T>::const_iterator
    List<T>
    ::begin() const {
        return iterator(queue.begin());
    }
    

    Hier hole ich von queue einen const_iterator, und will ihn auf einen iterator legen. Deswegen sagt er:

    main.cc:204:34: error: no matching function for call to ‘List<int>::Iterator<int>::Iterator(std::vector<int, std::allocator<int> >::const_iterator)’
    

    Kommt man an der Stelle überhaupt weiter, oder muß man zu Tricks wie diesen hier greifen: http://www.sj-vs.net/c-implementing-const_iterator-and-non-const-iterator-without-code-duplication/

    ?? Ich hasse Iteratoren. 😉



  • Hallo zusammen,

    also nun habe ich eine Version, welche funktioniert. 😃
    Hier anzuschauen, bei Interesse: http://codepad.org/yiB048WD (Feedback wie immer willkommen).

    Ich habe dazu ein wenig bei Herrn Stroustrup geschaut. Die Version, wie ich es eigentlich wollte, ein Delegate-Iterator, war selbst mit seinen Beispiel-Code nicht compilierfähig. Aber ne Idee habe ich bekommen. So ist es vielleicht nicht die effizientest Variante, aber geht immerhin. Ich hoffe es sind keine weiteren Fehler drin.

    Eine Frage hätte ich noch. Ich habe ja ne Funktion add(), welche zwei Iteratoren akzeptiert. Nun habe ich festgestellt, dass diese nicht so richtig mit den std::iteratoren zusammenarbeitet. Also ein Aufruf:

    List<int> tempList;
     std::vector<std::string> a;
     tempList.add(a.begin(), a.end());
    

    ergibt:

    main.cc:393:36: error: no matching function for call to ‘List<int>::add(std::vector<std::basic_string<char> >::iterator, std::vector<std::basic_string<char> >::iterator)’
    main.cc:393:36: note: candidates are:
    main.cc:212:1: note: List<T>& List<T>::add(const T&) [with T = int]
    main.cc:212:1: note:   candidate expects 1 argument, 2 provided
    main.cc:221:1: note: List<T>& List<T>::add(List<T>::iterator, List<T>::iterator) [with T = int; List<T>::iterator = List<int>::Iterator<int>]
    main.cc:221:1: note:   no known conversion for argument 1 from ‘std::vector<std::basic_string<char> >::iterator {aka __gnu_cxx::__normal_iterator<std::basic_string<char>*, std::vector<std::basic_string<char> > >}’ to ‘List<int>::iterator {aka List<int>::Iterator<int>}’
    

    Da noch nen Tip für mich???

    Ansonsten erst mal riesen dank für Eure super Hilfe!!!!!! 🙂

    sven_


  • Mod

    Du musst lernen, solche Fehlermeldungen zu lesen. Es steht schon alles erklaert in der Meldung. Es sieht bloss komplizierter aus, als es ist, wegen der Templates. Da hilft es, den ganzen Templatekram einfach erst einmal zu ignorieren:

    main.cc:393:36: error: no matching function for call to ‘List<int>::add(std::vector<std::basic_string<char> >::iterator, std::vector<std::basic_string<char> >::iterator)’
    

    ➡

    main.cc:393:36: error: no matching function for call to ‘List<int>::add(Blah, Blah)’
    

    (mit Blah = std::vector<std::basic_string<char> >::iterator)
    Aha, es gibt also keine passende Memberfunktion add von List<int>, so wie sie in Zeile 393 benutzt wird.

    main.cc:393:36: note: candidates are:
    

    Aber es gibt andere List<int>::add(), die nicht ganz passen:

    main.cc:212:1: note: List<T>& List<T>::add(const T&) [with T = int]
    main.cc:212:1: note:   candidate expects 1 argument, 2 provided
    

    Es gibt ein add, welches ein const int& erwartet. Klar passt dieses nicht, da es nur 1 Argument nimmt, anstatt 2. Aber es gibt noch einen anderen Kandidaten:

    main.cc:221:1: note: List<T>& List<T>::add(List<T>::iterator, List<T>::iterator) [with T = int; List<T>::iterator = List<int>::Iterator<int>]
    main.cc:221:1: note:   no known conversion for argument 1 from ‘std::vector<std::basic_string<char> >::iterator {aka __gnu_cxx::__normal_iterator<std::basic_string<char>*, std::vector<std::basic_string<char> > >}’ to ‘List<int>::iterator {aka List<int>::Iterator<int>}’
    

    😮 Wieder erst einmal vereinfachen:

    main.cc:221:1: note: List<T>& List<T>::add(Blupp, Blupp) 
    main.cc:221:1: note:   no known conversion for argument 1 from ‘Blah’ to ‘Blupp’
    

    Das ist doch schon verstaendlicher. Wir haben die passende Argumentenzahl, aber die Typen passen nicht. Jetzt koennen wir genauer auf die Typen gucken. Das 'Blupp' hier ist List<T>::iterator oder, wenn man alle typedefs usw. auswertet, dann bekommt man das was der Compiler in die eckigen Klammern am Ende geschrieben hat: List<T>::iterator = List<int>::Iterator<int>]. Das add erwartet also 2x List<int>::Iterator<int>, also deinen eigenen Iteratortypen.

    Was ist das Blah? std::vector<std::basic_string<char> >::iterator. Wir sehen jetzt schon, dass das nicht passt, da das der Iteratortyp des STL-vectors ist, nicht dein List-Iterator. Falls das nocht nicht gereicht haette, hat uns der Compiler wieder in Klammern gesagt, was das fuer ein Typ ist, wenn man lles bis zum Ende auswertet: __gnu_cxx::__normal_iterator<std::basic_string<char>*, std::vector<std::basic_string<char> > >
    Daraus koennen wir nicht mehr viel ablesen, das sind nur Details, wie der vector-Iterator intern funktioniert. Wir haben oben ja schon gesehen, was falsch ist.

    P.S.: Zusammenfassung: Du benutzt vector-Iteraoren, wo deine Funktionen deine eigenen Iteratoren erwarten. Du hast keine Konvertierungsfunktion, um einen deiner eigenen Iteratoren aus einem vector-Iterator zu erzeugen (sofern dies ueberhaupt Sinn macht).
    P.P.S.: Deine add-Funktion mit 2 Parametern soll wahrscheinlich ein Template sein. Sie nimmt nicht deine Iteratoren, sondern alle moeglichen Iteratoren. Es soltle dem add schliesslich egal sein, woher die Daten kommen. Siehe die ganzen Funktionen der STL, die das so machen. Z.B.:
    http://www.cplusplus.com/reference/vector/vector/insert/
    Da gibt es die Ueberladung

    template <class InputIterator>
        void insert (iterator position, InputIterator first, InputIterator last);
    

    So soll wahrscheinlich auch dein add aussehen. Diese Muster gibt es an ganz vielen Stellen und du solltest es auch benutzen. So kann man alle moeglivchen Objekte benutzen, sie muessen sich bloss iteratorartig verhalten. Das heisst, du kannst einen STL-Vector ohne Probleme mit Zeigern (Iteratoren sind verallgemeinerte Zeiger), Iteratoren aus anderen STL-containern, oder auch problemlos mit deinem eigenen List-Iterator fuettern. Das insert interessiert hier bloss, dass man den Iterator derefenzieren, kopieren, verlgeichen und mittels ++ erhoehen kann (das wird durch den Begriff "Inputiterator" ausgedrueckt).


Anmelden zum Antworten