简体   繁体   English

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

[英]How to return a generic iterator (independent of particular container)?

I'd like to design a class Foo that stores various data of different types and returns iterators over them. 我想设计一个类Foo ,它存储不同类型的各种数据,并通过它们返回迭代器。 It's supposed to be generic, so the user of Foo does not know how the data is stored ( Foo could be using std::set or std::vector or whatever). 它应该是通用的,因此Foo的用户不知道数据是如何存储的( Foo可能使用std::setstd::vector或其他)。

I'm tempted to write an interface like this: 我很想写一个这样的界面:

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

where Iterator is something like this (like iterators in .NET): Iterator就像这样(就像.NET中的迭代器一样):

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

But I know that kind of iterator is not standard in C++, and it's better to use iterators the way the STL does, so you can use the STL algorithms on them. 但是我知道这种迭代器在C ++中不是标准的,并且最好像STL一样使用迭代器,所以你可以在它们上使用STL算法。

How can I do that? 我怎样才能做到这一点? (Do I need iterator_traits by any chance?) (我有机会需要iterator_traits吗?)

Do you understand why the STL chose to put iterator implementation details in the header file? 您是否理解为什么STL选择将迭代器实现细节放在头文件中? JIT frameworks are able to inline across compilation units, but C++ can only inline within a compilation unit. JIT框架能够跨编译单元内联,但C ++只能在编译单元中内联。 Advancing through a sequence is much faster when inlined, the cost of a function call dominates actually traversing the data structure. 内联时推进序列的速度要快得多,函数调用的成本实际上主要是遍历数据结构。

If you really want to hide the implementation details, go ahead. 如果您真的想隐藏实现细节,请继续。 You could make an STL-compatible iterator that implements operator++ and operator!= and operator-> in terms of protected virtual functions, the Next, Done, and Value you've mentioned would be decent names. 你可以创建一个STL兼容的迭代器来实现operator ++和operator!=和operator->就受保护的虚函数而言,你提到的Next,Done和Value都是不错的名字。 Just expect to pay for the encapsulation with lower performance. 只希望以较低的性能为封装付费。

Use a typedef to return an boost::iterator_range . 使用typedef返回boost::iterator_range For example (never mind the names), 例如(不要介意名字),

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

The user code will be 用户代码将是

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

This is not generic, but the same idea can be used with templates. 这不是通用的,但同样的想法可以用于模板。

A c++ class with iterators has to provide at least two functions if they have to work with the std library 带有迭代器的c ++类必须至少提供两个函数,如果它们必须使用std库

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

The iterator has to overload the increment operators, equals and * 迭代器必须重载增量运算符,等于和*

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

You can use the iterator class to wrap around the iterator of the internal storage to ensure that the user is limited to these methods. 您可以使用迭代器类来包装内部存储的迭代器,以确保用户仅限于这些方法。

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

Where T is the type of your container.(The class is incomplete and I may have mixed something up) 其中T是你的容器的类型。(这个类是不完整的,我可能混淆了一些东西)

It almost looks like you're trying to create container-independent code, which is not (in general) a good idea, unless you are writing an algorithm which can operate solely with iterators. 它几乎看起来像你正在尝试创建与容器无关的代码,这通常不是一个好主意,除非你正在编写一个只能用迭代器操作的算法。 (See Scott Myers Effective STL Item 2: Beware the illusion of container-independent code) (参见Scott Myers Effective STL第2项:注意与容器无关的代码的错觉)

The problem is that most of the standard containers do not provide overlapping functionality. 问题是大多数标准容器不提供重叠功能。 If you're writing code for a particular container, assume you're writing code for that container. 如果您正在为特定容器编写代码,请假设您正在为该容器编写代码。 Don't bother trying to make it container-independent. 不要试图使其与容器无关。

To fulfill the requirement that the particular container (vector, set, ...) is not mentioned in the header file, and the user will be able to iterate over all strings, is to use the visitor pattern . 为了满足头文件中未提及特定容器(vector,set,...)以及用户将能够遍历所有字符串的要求,使用访问者模式 The downside, of course, is that the user won't be able to use the STL algorithms on the strings. 当然,缺点是用户将无法在字符串上使用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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM