返回'任何类型的输入迭代器'而不是vector :: iterator或list :: iterator

时间:2008-09-24 13:07:35

标签: c++ stl iterator

假设我想在C ++中实现一个数据结构来存储面向图。由于STL容器,Arcs将存储在节点中。我希望用户能够以类似STL的方式迭代节点的弧。

我遇到的问题是我不想在Node类(实际上是一个抽象基类)中公开我将在具体类中实际使用的STL容器。因此,我不希望我的方法返回std :: list :: iterator或std :: vector :: iterator ...

我试过了:

class Arc;

typedef std::iterator<std::random_access_iterator_tag, Arc*> ArcIterator;  // Wrong!

class Node {
public:
  ArcIterator incomingArcsBegin() const {
    return _incomingArcs.begin();
  }
private:
  std::vector<Arc*> _incomingArcs;
};

但这不正确,因为vector :: const_iterator不能用于创建ArcIterator。那么这个ArcIterator可以是什么?

我发现这篇论文关于Custom Iterators for the STL,但它没有帮助。今天我一定有点沉重......;)

10 个答案:

答案 0 :(得分:8)

试试这个:

class Arc;
class Node {
private:
  std::vector<Arc*> incoming_;
public:
  typedef std::vector<Arc*>::iterator iterator;
  iterator incoming_arcs_begin()
  { return incoming_.begin(); }
};

在其余代码中使用Node :: iterator。当/如果更改容器,则必须在单个位置更改typedef。 (您可以使用额外的typedef进一步进行存储,在本例中为vector。)

至于const问题,要么将vector的const_iterator定义为迭代器,要么将vector迭代器类型(const和非const版本)定义为vector。

答案 1 :(得分:3)

查看Adobe的any_iterator:此类使用一种名为 type erase 的技术,通过该技术,底层迭代器类型隐藏在抽象接口后面。注意:由于虚拟调度,使用any_iterator会导致运行时损失。

答案 2 :(得分:1)

我想认为应该有一种方法可以通过直接STL来做到这一点,类似于你想要做的事情。

如果没有,您可能希望使用boost's iterator facades and adaptors来定义自己的迭代器或将其他对象调整为迭代器。

答案 3 :(得分:1)

要隐藏迭代器基于std::vector<Arc*>::iterator的事实,您需要一个委托给std::vector<Arc*>::iterator的迭代器类。 std::iterator不会这样做。

如果查看编译器的C ++标准库中的头文件,您可能会发现std::iterator本身并不是很有用,除非您只需要为{{1}定义typedef的类},iterator_category

正如Doug T.在他的回答中提到的,boost库有一些类,可以更容易地编写迭代器。特别是,如果您希望迭代器在取消引用而不是value_type时返回Arc,那么boost::indirect_iterator可能会有所帮助。

答案 4 :(得分:1)

考虑使用Visitor Pattern并反转关系:不是向图表结构询问数据容器,而是将图形作为仿函数,让图形将该仿函数应用于其数据。

访问者模式是图表上常用的模式,请查看关于访问者概念的boost图表库文档。

答案 5 :(得分:0)

如果你真的不希望该类的客户端知道它使用了下面的向量,但仍然希望它们能够以某种方式迭代它,那么你很可能需要创建一个转发所有它的类std :: vector :: iterator。

的方法

另一种方法是根据Node下面应该使用的容器类型来模拟Node。然后客户特别知道它正在使用什么类型的容器,因为他们告诉他们使用它。

就我个人而言,我认为将矢量封装远离用户通常是有意义的,但仍然提供其大部分(甚至一些)接口。它太薄了封装层,无法真正提供任何好处。

答案 6 :(得分:0)

我查看了头文件VECTOR。

vector<Arc*>::const_iterator

的typedef
allocator<Arc*>::const_pointer

这可能是你的ArcIterator吗?像:

typedef allocator<Arc*>::const_pointer ArcIterator;

答案 7 :(得分:0)

你可以对Node类进行模板化,并在其中输入iterator和const_iterator。

例如:

class Arc {};

template<
  template<class T, class U> class Container = std::vector,
  class Allocator = std::allocator<Arc*>
>
class Node
{
  public:
    typedef typename Container<Arc*, Allocator>::iterator ArcIterator;
    typedef typename Container<Arc*, Allocator>::Const_iterator constArcIterator;

    constArcIterator incomingArcsBegin() const {
      return _incomingArcs.begin();
    }

    ArcIterator incomingArcsBegin() {
      return _incomingArcs.begin();
    }
  private:
    Container<Arc*, Allocator> _incomingArcs;
};

我没有尝试过这段代码,但它给了你这个想法。但是,您必须注意使用ConstArcIterator只会禁止修改指向Arc的指针,而不是修改Arc本身(例如通过非const方法)。

答案 8 :(得分:0)

C ++ 0x允许您使用automatic type determination执行此操作。

在新标准中,这个 for (vector::const_iterator itr = myvec.begin(); itr != myvec.end(); ++itr
可以用这个替换 for (auto itr = myvec.begin(); itr != myvec.end(); ++itr)

出于同样的原因,您将能够返回任何适当的迭代器,并将其存储在auto变量中。

在新标准出现之前,您必须对您的类进行模板化,或者提供抽象接口来访问列表/向量的元素。例如,您可以通过在成员变量中存储迭代器来实现,并提供成员函数,如begin()next()。当然,这意味着一次只能有一个循环可以安全地迭代你的元素。

答案 9 :(得分:0)

因为std::vector保证有连续的存储空间,所以这样做应该是完美的:

class Arc;
typedef Arc* ArcIterator;

class Node {
public:
    ArcIterator incomingArcsBegin() const {
        return &_incomingArcs[0]
    }

    ArcIterator incomingArcsEnd() const {
        return &_incomingArcs[_incomingArcs.size()]
    }
private:
    std::vector<Arc*> _incomingArcs;
};

基本上,指针的功能就像随机访问迭代器一样,它们是足够的替代品。