Konstruktoraufruf mit Iteratoren



  • Hallo,

    ich habe ein STL-Set in einer eigenen Klasse mySet gekapselt, da ich dem Set ein paar Extrafunktionen spendieren wollte.

    Um die Initialisierung per Iteratoren möglich zu machen, habe ich folgenden Konstruktor für mySet geschrieben:

    mySet(it_t first, it_t last) : content(set<t>(first, last))  {}
    

    (es gilt it_t = set<t>::iterator)

    Der Aufruf dafür wäre:

    mySet<char> charSet(charArray, charArray+5);
    

    Er schlägt fehl weil:

    error: no matching function for call to ‘mySet<char>::mySet(char [5], char*)
    

    Ergo die Funktion wird nicht gefunden.

    Die offensichtliche "Lösung" ist jetzt, einen Konstruktor der Art "mySet(char*, char*).." zu schreiben, aber das ist ja dann nicht generisch. Kriegt man das ganze auch generisch hin? Vielen Dank für eure Hilfe.



  • mach ein Template draus:

    template <class Iter>
    mySet(Iter first, Iter last) : content(first, last)  {}
    

    ich nehme mal an, dass conten ein set<T> ist, da brauchste nicht n temporäres von zu erzeugen.


  • Mod

    Du könntest auch einen eigenen Iterator anbieten, der die entsprechenden Konvertierungen besitzt. Hier mal am Beispiel eines erweiterten vectors:

    template <typename T> struct my_vector: public vector<T>
    {
      struct iterator
      {
        iterator(T* foo){}
      };
      my_vector(iterator beg,iterator end)
      {
        // Irgendwas
      }
    };
    
    int main () {
      char foo[6];
      my_vector<char> bar(foo,foo+5);
    }
    


  • Vielen Dank schon mal, die Lösung von Pumuckl kompiliert.

    Einen Wermutstropfen gibt es da aber, denn zur Compilezeit übernimmt dieser Konstruktor dann Aufrufe durch beliebige Typen. D.h. folgendes wäre möglich:

    int integers[] = {0,1,2};
    mySet<char>(integers, integers+2);
    

    Der Vorschlag von SeppJ habe ich versucht einzubinden, indem ich nur das Snippet zu meiner bestehenden Klasse hinzugefügt habe:

    struct iterator {
        iterator(T* foo){}
    };
    

    Leider wirft der Compiler bei der mittleren Zeile einige Fehler (z.B. /usr/include/c++/4.3/bits/stl_tree.h:1295: error: no match for ‘operator*’ in ‘*__first’)


  • Mod

    xorand schrieb:

    Der Vorschlag von SeppJ habe ich versucht einzubinden, indem ich nur das Snippet zu meiner bestehenden Klasse hinzugefügt habe:

    struct iterator {
        iterator(T* foo){}
    };
    

    Leider wirft der Compiler bei der mittleren Zeile einige Fehler (z.B. /usr/include/c++/4.3/bits/stl_tree.h:1295: error: no match for ‘operator*’ in ‘*__first’)

    Das liegt daran, weil ich nur ein unvollständiges Beispiel geliefert habe. Du musst die zu einem Iterator gehörenden Methoden schon implementieren oder erben, damit das funktioniert.



  • xorand schrieb:

    Einen Wermutstropfen gibt es da aber, denn zur Compilezeit übernimmt dieser Konstruktor dann Aufrufe durch beliebige Typen. D.h. folgendes wäre möglich:

    int integers[] = {0,1,2};
    mySet<char>(integers, integers+2);
    

    Ist doch wunderbar dass das geht. Sind ja auch Iteratoren, und warum sollte man es hier anders machen als die STL und auf bestimmte Iteratorentypen beschränken?



  • xorand schrieb:

    Vielen Dank schon mal, die Lösung von Pumuckl kompiliert.

    Einen Wermutstropfen gibt es da aber, denn zur Compilezeit übernimmt dieser Konstruktor dann Aufrufe durch beliebige Typen. D.h. folgendes wäre möglich:

    int integers[] = {0,1,2};
    mySet<char>(integers, integers+2);
    

    Wäre es. Und es würde die ints in chars konvertieren, so wie mans erwartet. Wenn du wirklich nur Iteratoren auf char haben möchtest, geht das auch. Festhalten:

    #include <boost/type_traits/is_same.hpp>
    #include <boost/utility/enable_if.hpp>
    #include <iterator>
    
    /* ... */
    
    template <class T>
    class mySet
    {
      template <class Iter>
      struct IterValueTypeIsT : public boost::is_same<typename std::iterator_traits<Iter>::value_type, T>
      {};
    
    public:
      template <class Iter>
      mySet(Iter first, boost::enable_if<IterValueTypeIsT<Iter>, Iter>::type last) : content(first, last)  {}
    
    /* ... */
    };
    

    Zur Erklärung: IterValueTypeIsT erbt entweder von boost::true_type oder boost::false_type, je nachdem ob der value_type von Iter ein T ist oder nicht. Dazu muss std::iterator_traits<Iter> compilieren, d.h. entweder Iter hat ein member-typedef value_type oder ist ein Pointer oder std::iterator_traits wurde entsprechend spezialisiert.
    enable_if compiliert nur wenn IterValueTypeIsT von true_type erbt, in dem Fall ist der membertypedef type gleich dem zweiten templateparameter, also hier Iter.
    Das ganze Konstrukt compiliert also genau dann wenn std::iterator_traits<Iter>::value_type T ergibt und ist dann genau das gleiche was ich vorher schon vorgeschlagen hatte 😉

    /edit: das ::type gehörte ans enable_if, nicht ans IterValueTypeIsT



  • Super, das war was ich wollte. Vielen Dank für die schnellen Antworten. Boost ftw 🙂


Anmelden zum Antworten