Generischer Iterator



  • Hallo!

    Ich habe 2 Schleifen ueber STL Container. Einmal ueber eine Map und einmal ueber eine List:

    map<int, Foo>::iterator it1 = myMap.begin();
    for(; it1 != myMap.end(); ++it1) {
       const Foo& f = it1->second;  // A
       ...
    }
    
    list<Foo>::iterator it2 = myList.begin();
    for(; it2 != myList.end(); ++it2) {
        const Foo& f = *it2; // B
    }
    

    In beiden Schleifen hol ich mir das Foo Objekt und arbeite damit. Nur die Dereferenzierung in Zeilen A und B ist anders. Ich will es so haben, das beide Schleifen gleich aussehen. Aber ich weiss nicht wie das geht, da ich bei einem Map Iterator ja anders an das Foo komme als bei einem list iterator.
    Wie koennte ich das generalisieren?



  • template<class T>
    const Foo& GetMyFoo(T& Iter)
    {
      static_assert(false, "Ich kenne den Iterator nicht...");
    }
    template<>
    const Foo& GetMyFoo<std::list<Foo>::iterator>(std::list<Foo>::iterator& Iter)
    {
      return *Iter;
    }
    template<class T>
    const Foo& GetMyFoo<std::map<int, Foo>::iterator>(std::map<int, Foo>::iterator& Iter)
    {
      return Iter->second;
    }
    

    EDIT: Relativ schmierige Lösung, weil er bei std::map nur int unterstützt und weil die const_ - und reverse_ -Versionen der Iterator nicht kennt.



  • template<class T>
    T& second(T& t) { return t; }
    
    template<class T, class U>
    U& second(std::pair<T, U>& x) { return x.second; }
    
    template<class Iter>
    Foo& get_foo(Iter& iter) { return second(*iter); }
    


  • EOutOfResources schrieb:

    template<class T>
    const Foo& GetMyFoo(T& Iter)
    {
      static_assert(false, "Ich kenne den Iterator nicht...");
    }
    template<>
    const Foo& GetMyFoo<std::list<Foo>::iterator>(std::list<Foo>::iterator& Iter)
    {
      return *Iter;
    }
    template<class T>
    const Foo& GetMyFoo<std::map<int, Foo>::iterator>(std::map<int, Foo>::iterator& Iter)
    {
      return Iter->second;
    }
    

    EDIT: Relativ schmierige Lösung, weil er bei std::map nur int unterstützt und weil die const_ - und reverse_ -Versionen der Iterator nicht kennt.

    Das hätte man auch mit normaler Funktionsüberladung machen können...



  • ipsec schrieb:

    template<class T>
    T& second(T& t) { return t; }
    
    template<class T, class U>
    U& second(std::pair<T, U>& x) { return x.second; }
    
    template<class Iter>
    Foo& get_foo(Iter& iter) { return second(*iter); }
    

    Perfekt! Vielen Dank! 👍



  • Ohne gewähr...

    #include <iterator>
    #include <map>
    #include <list>
    #include <iostream>
    
    using namespace std;
    
    template <class Pair> struct PairTraits;
    template <class First, class Second>
    struct PairTraits<std::pair<First, Second> >
    {
      typedef First first_t;
      typedef Second second_t;
    };
    
    template <class Iterator>
    struct PairAccessSecondPolicy
    {
      typedef typename PairTraits<typename iterator_traits<Iterator>::value_type>::second_t value_type;
      typedef value_type& reference;
      typedef value_type* pointer;
    
      static reference access(Iterator const& it)
      {
        return it->second;
      }
      static pointer dereference(Iterator const& it)
      {
        return &(it->second);
      }
    };
    
    template <class Iterator, class AccessPolicy>
    struct AccessingIterator : public Iterator
    {
      typedef typename iterator_traits<Iterator>::difference_type difference_type;
      typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
      typedef typename AccessPolicy::value_type value_type;
      typedef typename AccessPolicy::reference reference;
      typedef typename AccessPolicy::pointer pointer;
    
      AccessingIterator(Iterator const& it) : Iterator(it) {}
    
      reference operator* ()
      {
        return AccessPolicy::access(*this);
      }
    
      pointer operator-> ()
      {
        return AccessPolicy::dereference(*this);
      }
    };
    
    template <class Map>
    struct MapSecondAccessor
    {
      typedef typename Map::iterator MapIter;
      typedef AccessingIterator<MapIter, PairAccessSecondPolicy<MapIter> > type;
    };
    
    struct Foo { int i; };
    
    int main()
    {
      map<int, Foo> myMap;
      list<Foo> myList;
      for (int j = 0; 0 < 5; ++j)
      {
        myMap[j].i = 5-j;
        myList.push_back(myMap[j]);
      }
    
      MapSecondAccessor<map<int, Foo> >::type it1 = myMap.begin();
      for(; it1 != myMap.end(); ++it1) {
        const Foo& f = *it1;  // A
        cout << f.i << ' ';
      }
      cout << '\n';
    
      list<Foo>::iterator it2 = myList.begin();
      for(; it2 != myList.end(); ++it2) {
        const Foo& f = *it2; // B
        cout << f.i << ' ';
      }
      cout << '\n';
    }
    

    compiliert zumindest - weiter testen konnte ichs nicht 😉



  • Man kann auch einfach boost::make_transform_iterator verwenden.
    Beispiel:

    http://stackoverflow.com/questions/259240/iterator-adapter-to-iterate-just-the-values-in-a-map


Log in to reply