开发者

Designing an iterator wrapper

I wrote a class that wraps an iterator and returns transformed values on demand:

// iterator-wrapper.h
template<class Iter, class Val, class Fct>
class IteratorWrapper {
  Iter cur_;
  const Iter last_;
  const Fct fct_;
public:
  IteratorWrapper(Iter first, Iter last, const Fct fct)
    : cur_(first), last_(last), fct_(fct) 
  {}
  const Val Value() const {return fct_(*cur_);}
  void Next() {++cur_;}
  bool Done() const {return cur_ == last_;}
};

Now, a class can use it to return iterators over some functions of its data:

// mydata.h
#include <map>
#include "iterator-wrapper.h"

class MyData {

  struct GetFirst {
    template<class T1, class T2>
      const T1& operator()(const std::pair<T1,T2>& aPair) const {
      return aPair.first;
    }
  };
  struct GetSecond {
    template<class T1, class T2>
      const T2& operator()(const std::pair<T1,T2>& aPair) const {
      return aPair.second;
    }
  };

  typedef std::string Key;
  typedef int Val;
  typedef std::map<Key, Val> Map;
  typedef Map::const_iterator MapIter;
  Map m_;

public:

  typedef IteratorWrapper<MapIter, Key, GetFirst> KeysIter;
  typedef IteratorWrapper<MapIter, Val, GetSecond> ValuesIter;

  MyData() { // add some data
    m_["foo"] = 1;
    m_["bar"] = 2;
  }

  KeysIter GetKeys() const {
    return KeysIter(m_.begin(), m_.end(), GetFirst());
  }

  ValuesIter GetValues() const {
    return ValuesIter(m_.begin(), m_.end(), GetSecond());
  }
 };

And, here is an example usage:

#include <iostream>
#include "iterator-wrapper-data.h"

 int main() {
   MyData d;

   std::cout << "KEYS:" << std::endl;
   MyData::KeysIter kit = d.GetKeys();
   for(; !kit.Done(); kit.Next()){
     std::cout << kit.Value() << std::endl;
   }

   std::cout << "VALUES:" << std::endl;
   MyData::开发者_开发问答ValuesIter vit = d.GetValues();
   for(; !vit.Done(); vit.Next()){
     std::cout << vit.Value() << std::endl;
   }
   return 0;
 }

I have several questions about this:

  1. Is this a reasonable design, or could one do it more succinctly, maybe using STL or boost stuff? (I know there is a boost::iterator_facade but I found that code way more complicated than necessary and I'm not sure if it does exactly what I want here.)

  2. Why doesn't std::map contain something like that to begin with (I mean a keys() function that returns an iterator over keys, etc.)? (Or does it?)

  3. What is wrong with returning a reference, like const Val& Value() const (as opposed returning by value, as in the first listing above)?


You were on the right track by taking a look at boost::iterator_facade, but you're even better off with boost::transform_iterator.


About your second question: the Boost RangeEx library (zip file) (not yet distributed with Boost but accepted for future inclusion) contains range adaptors for iterating over map keys and values:

int main() {

   std::map<int, std::string> m;
   m[1] = "one";
   m[2] = "two";

   std::cout << "KEYS:" << std::endl;
   BOOST_FOREACH(int k, m | boost::adaptors::map_keys) {
       std::cout << k << std::endl;
   }

   std::cout << "VALUES:" << std::endl;
   BOOST_FOREACH(std::string const & v, m | boost::adaptors::map_values) {
       std::cout << v << std::endl;
   }
   return 0;
}


It doesn't seem unreasonable to me, but I don't think it's really necessary. There are select1st and select2nd, from the SGI STL Guide:

int main()
{
  map<int, double> M;
  M[1] = 0.3;
  M[47] = 0.8;
  M[33] = 0.1;

  transform(M.begin(), M.end(), ostream_iterator<int>(cout, " "),
            select1st<map<int, double>::value_type>());
  // The output is  1 33 47.
}

Oh, and of course, there are compose1 and compose2 when you want to do more than just feed them to some insert or output iterator directly.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜