如何返回通用迭代器(独立于特定容器)?

时间:2010-02-20 20:20:08

标签: c++ stl iterator pimpl-idiom

我想设计一个类Foo,它存储不同类型的各种数据,并通过它们返回迭代器。它应该是通用的,因此Foo的用户不知道数据的存储方式(Foo可能正在使用std::setstd::vector或其他任何内容。

我很想写一个这样的界面:

class Foo {
  class FooImpl;
  FooImpl* impl_;
public:
  const Iterator<std::string>& GetStrings() const;
  const Iterator<int>& GetInts() const;
};

其中Iterator就像这样(就像.NET中的迭代器一样):

template<class T>
class Iterator {
public:
  const T& Value() const = 0;
  bool Done() const = 0;
  void Next() = 0;
};

但是我知道这种迭代器在C ++中不是标准的,并且最好像STL一样使用迭代器,所以你可以在它们上使用STL算法。

我该怎么做? (我是否需要iterator_traits?)

5 个答案:

答案 0 :(得分:4)

您是否理解为什么STL选择将迭代器实现细节放在头文件中? JIT框架能够跨编译单元内联,但C ++只能在编译单元中内联。在内联时,推进序列的速度要快得多,函数调用的成本实际上主要是遍历数据结构。

如果您真的想隐藏实施细节,请继续。你可以创建一个STL兼容的迭代器来实现operator ++和operator!=和operator-&gt;就受保护的虚拟功能而言,您提到的Next,Done和Value将是不错的名称。只是希望以较低的性能为封装付费。

答案 1 :(得分:1)

使用typedef返回boost::iterator_range。例如(不要介意名字),

class Container
{
     typedef std::vector<int> Collection; 

     public:
     typedef boost::iterator_range<Collection::iterator> CollectionRange;
     typedef Collection::iterator CollectionIterator;
     Range range() const {
          return make_iterator_range(collection_.begin(), collection_.end());
     }

     private:
     Collection collection_;          
};

用户代码为

Container c;
// ...
FOREACH(int i, c.range()) { //... }
Container::Range r = c.range();
for(Container::iterator j = r.begin(); j!= r.end(); j++) { // ... }

这不是通用的,但同样的想法可以与模板一起使用。

答案 2 :(得分:1)

带有迭代器的c ++类必须提供至少两个函数,如果它们必须使用std库

iterator begin() //returns an iterator at starting pos
iterator end() //returns an iterator one past end or just invald

迭代器必须重载增量运算符,equals和*

iterator operator++()
iterator operator==()//make sure that an invalid iterator equals end()
T& operator*()

您可以使用迭代器类来包装内部存储的迭代器,以确保用户仅限于这些方法。

template <typename T> iter
{
   iter(T::iterator& intern)
   T::value_type& operator*(){return *intern}
  iter operator++(){return iter(++intern);}
  bool operator==(iter const& other)const{return intern == other.intern;}
}

其中T是你的容器的类型。(这个类是不完整的,我可能混淆了一些东西)

答案 3 :(得分:1)

这几乎看起来你正在尝试创建与容器无关的代码,除非你正在编写一个只能用迭代器操作的算法,否则它(通常)不是一个好主意。 (参见Scott Myers Effective STL第2项:注意与容器无关的代码的错觉)

问题是大多数标准容器不提供重叠功能。如果您正在为特定容器编写代码,请假设您正在为该容器编写代码。不要试图让它与容器无关。

答案 4 :(得分:-1)

要满足头文件中未提及特定容器(vector,set,...)并且用户能够遍历所有字符串的要求,请使用访问者模式< / strong>即可。当然,缺点是用户将无法在字符串上使用STL算法。

// foo.h
class StringVisitor {
public:
  void accept(const std::string& str) {
    std::cout << str << std::endl;
  }
};
class Foo {
  class Impl;
  Impl* impl_;
public:
  Foo();
  ~Foo();
  void VisitStrings(StringVisitor v) const;
};

// foo.cc
class Foo::Impl {
  typedef std::vector<std::string> StringContainer;
  StringContainer str_;
public:
  Impl() {
    str_.push_back("a");
    str_.push_back("b");
  }
  void VisitStrings(StringVisitor v) const {
    for(StringContainer::const_iterator it = str_.begin();
    it != str_.end(); ++it){
      v.accept(*it);
    }
  }  
};

Foo::Foo() : impl_(new Impl()) {}
Foo::~Foo() {delete impl_;}
void Foo::VisitStrings(StringVisitor v) const {
  impl_->VisitStrings(v);
}

// main.cc
int main() {
  Foo foo;
  foo.VisitStrings(StringVisitor());
  return 0;
}