【问题标题】:Designing an iterator wrapper设计迭代器包装器
【发布时间】:2010-02-07 21:06:28
【问题描述】:

我写了一个类,它封装了一个迭代器并根据需要返回转换后的值:

// 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_;}
};

现在,一个类可以使用它在其数据的某些函数上返回迭代器:

// 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());
  }
 };

还有,这里是一个示例用法:

#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;
 }

我对此有几个问题:

  1. 这是一个合理的设计,还是可以更简洁地做到这一点,也许使用 STL 或 boost 的东西? (我知道有一个boost::iterator_facade,但我发现代码比必要的复杂得多,我不确定它是否完全符合我的要求。)

  2. 为什么std::map 开头不包含类似的内容(我的意思是keys() 函数,它返回键的迭代器等)? (或者是吗?)

  3. 返回引用有什么问题,比如const Val&amp; Value() const(而不是按值返回,如上面的第一个清单)?

【问题讨论】:

  • 我发现了这个相关的问题:stackoverflow.com/questions/681943/…
  • 您到底为什么要构建自己的迭代器接口?你所做的基本上没问题(Boost 有一个 transform_iterator 做同样的事情,但是看在上帝的份上,你正在编写一个新的迭代器,给它一个迭代器的接口。

标签: c++ stl iterator wrapper


【解决方案1】:

通过查看 boost::iterator_facade,您走在了正确的轨道上,但使用 boost::transform_iterator 会更好。

【讨论】:

    【解决方案2】:

    关于您的第二个问题:Boost RangeEx 库 (zip file)(尚未随 Boost 一起分发,但已接受以供将来包含)包含用于迭代映射键和值的范围适配器:

    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;
    }
    

    【讨论】:

      【解决方案3】:

      这对我来说似乎不无道理,但我认为这不是真的必要。有select1st和select2nd,来自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.
      }
      

      哦,当然,还有 compose1 和 compose2,当您想要做的不仅仅是将它们直接提供给某个插入或输出迭代器。

      【讨论】:

      • 如果你有它们,但它们不是 C++ 标准的一部分,它们很好。
      • 嗯,它们非常普遍,如果需要更高的可移植性,当然很容易提供。
      猜你喜欢
      • 1970-01-01
      • 2019-11-27
      • 2014-04-24
      • 2016-06-23
      • 2011-03-29
      • 2015-09-30
      • 2015-05-17
      • 2021-01-19
      • 2014-04-16
      相关资源
      最近更新 更多