返回“任何类型的输入迭代器” 而不是向量::迭代器或列表::迭代器

发布于 2024-07-05 20:23:01 字数 742 浏览 4 评论 0原文

假设我想用 C++ 实现一个数据结构来存储定向图。 由于 STL 容器,弧将存储在节点中。 我希望用户能够以类似 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 会是什么?

我找到了这篇关于 STL 的自定义迭代器 的论文,但它没有帮助。 我今天一定有点重...;)

Suppose I want to implement in C++ a data-structure to store oriented graphs. Arcs will be stored in Nodes thanks to STL containers. I'd like users to be able to iterate over the arcs of a node, in an STL-like way.

The issue I have is that I don't want to expose in the Node class (that will actually be an abstract base class) which STL container I will actually use in the concrete class. I therefore don't want to have my methods return std::list::iterator or std::vector::iterator...

I tried this:

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

But this is not correct because a vector::const_iterator can't be used to create an ArcIterator. So what can be this ArcIterator?

I found this paper about Custom Iterators for the STL but it did not help. I must be a bit heavy today... ;)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

且行且努力 2024-07-12 20:23:01

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

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

Well because std::vector is guaranteed to have contiguous storage, it should be perfect fine to do this:

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

Basically, pointers function enough like random access iterators that they are a sufficient replacement.

时光匆匆的小流年 2024-07-12 20:23:01

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

如果没有,您可能需要考虑使用 boost 的迭代器外观和适配器,您可以在其中定义自己的迭代器或将其他对象适配到迭代器中。

I want to think there should be a way to do this through straight STL, similar to what you are trying to do.

If not, you may want to look into using boost's iterator facades and adaptors where you can define your own iterators or adapt other objects into iterators.

贪恋 2024-07-12 20:23:01

要隐藏迭代器基于 std::vector::iterator 的事实,您需要一个委托给 std::vector的迭代器类: :迭代器std::iterator 不会执行此操作。

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

正如 Doug T. 在他的回答中提到的,boost 库有一些类可以更轻松地编写迭代器。 特别是 boost::indirect_iteratorArc 而不是 Arc*,code> 可能会有所帮助。

To hide the fact that your iterators are based on std::vector<Arc*>::iterator you need an iterator class that delegates to std::vector<Arc*>::iterator. std::iterator does not do this.

If you look at the header files in your compiler's C++ standard library, you may find that std::iterator isn't very useful on its own, unless all you need is a class that defines typedefs for iterator_category, value_type, etc.

As Doug T. mentioned in his answer, the boost library has classes that make it easier to write iterators. In particular, boost::indirect_iterator might be helpful if you want your iterators to return an Arc when dereferenced instead of an Arc*.

终遇你 2024-07-12 20:23:01

考虑使用 访客模式 并反转关系:而不是向图形结构询问容器数据,您给图一个函子,并让图将该函子应用于其数据。

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

Consider using the Visitor Pattern and inverting the relationship: instead of asking the graph structure for a container of data, you give the graph a functor and let the graph apply that functor to its data.

The visitor pattern is a commonly used pattern on graphs, check out boost's graph library documentation on visitors concepts.

墨小沫ゞ 2024-07-12 20:23:01

如果您确实不希望该类的客户端知道它在下面使用向量,但仍然希望他们能够以某种方式迭代它,那么您很可能需要创建一个将其所有方法转发到 std 的类::向量::迭代器。

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

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

If you really don't want the client's of that class to know that it uses a vector underneath, but still want them to be able to somehow iterate over it, you most likely will need to create a class that forwards all its methods to std::vector::iterator.

An alternative would be to templatize Node based on the type of container it should use underneath. Then the clients specifically know what type of container it is using because they told them to use it.

Personally I don't think it usually makes sense to encapsulate the vector away from the user, but still provide most (or even some) of its interface. Its too thin of an encapsulation layer to really provide any benefit.

瞄了个咪的 2024-07-12 20:23:01

我查看了头文件 VECTOR.h

vector<Arc*>::const_iterator

是一个typedef,

allocator<Arc*>::const_pointer

它是你的ArcIterator吗? 喜欢:

typedef allocator<Arc*>::const_pointer ArcIterator;

I looked in the header file VECTOR.

vector<Arc*>::const_iterator

is a typedef for

allocator<Arc*>::const_pointer

Could that be your ArcIterator? Like:

typedef allocator<Arc*>::const_pointer ArcIterator;
惜醉颜 2024-07-12 20:23:01

您可以模板化 Node 类,并在其中 typedef 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 本身(例如通过非常量方法)。

You could templatize the Node class, and typedef both iterator and const_iterator in it.

For example:

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

I haven't tried this code, but it gives you the idea. However, you have to notice that using a ConstArcIterator will just disallow the modification of the pointer to the Arc, not the modification of the Arc itself (through non-const methods for example).

天煞孤星 2024-07-12 20:23:01

C++0x 将允许您通过自动类型确定来完成此操作。

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

同样,您将能够返回任何合适的迭代器,并将其存储起来在 auto 变量中。

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

C++0x will allow you do this with automatic type determination.

In the new standard, this
for (vector::const_iterator itr = myvec.begin(); itr != myvec.end(); ++itr
can be replaced with this
for (auto itr = myvec.begin(); itr != myvec.end(); ++itr)

By the same token, you will be able to return whatever iterator is appropriate, and store it in an auto variable.

Until the new standard kicks in, you would have to either templatize your class, or provide an abstract interface to access the elements of your list/vector. For instance, you can do that by storing an iterator in member variable, and provide member functions, like begin() and next(). This, of course, would mean that only one loop at a time can safely iterate over your elements.

深爱不及久伴 2024-07-12 20:23:01

试试这个:

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 进行存储,在本例中为向量。)

至于 const 问题,要么将向量的 const_iterator 定义为迭代器,要么将双迭代器类型(const 和非 const 版本)定义为矢量确实如此。

Try this:

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

And use Node::iterator in the rest of the code. When/if you change the container, you have to change the typedef in a single place. (You could take this one step further with additional typedef for the storage, in this case vector.)

As for the const issue, either define vector's const_iterator to be your iterator, or define double iterator types (const and non-const version) as vector does.

私藏温柔 2024-07-12 20:23:01

看看 Adob​​e 的 any_iterator:这个类使用了一种称为类型擦除,通过它,底层迭代器类型被隐藏在抽象接口后面。 请注意:使用 any_iterator 会因虚拟调度而导致运行时损失。

Have a look at Adobe's any_iterator: this class uses a technique called type erase by which the underyling iterator type is hidden behind an abstract interface. Beware: the use of any_iterator incurs a runtime penalty due to virtual dispatching.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文