【问题标题】:Iterator for custom container自定义容器的迭代器
【发布时间】:2021-09-15 17:16:54
【问题描述】:

在我的项目中,我有一些自定义类,大致结构如下。

Point,表示一个 2D int 坐标

struct Point {
  int x;
  int y;
}

Line,表示两点之间画的一条线

class Line {
    Point start;
    Point end;
}

多边形,表示由点数定义的多边形。

class Polygon {
  private:
    std::vector<Point> _vertices;
}

我正在尝试为 Polygon 实现一个自定义迭代器,目标是如下语法:

Polygon aPolygon;
Point somePoint;
for( auto line_it = aPolygon.begin(); line_it != aPolygon.end(); line_it++ ){
    if( line_it->includes(somePoint) ){
        // Do something
    }
}

在对 Polygon 进行迭代时,第 n 个元素将是
Line( _vertices[n], _vertices[n+1] ),
最后一个是
Line( _vertices[_vertices.size() - 1], _vertices[0] )

如何实现这样的迭代器,或以其他方式获得具有可接受性能的类似语法?

我正在查看类似的问题,但我还没有找到一个足够类似的问题来提供全面的答案。 如果可能的话,我更喜欢使用 c++20 标准的 STL 解决方案。


我意识到这个问题不必要地含糊不清,更具体地说,我问的是如何为我的迭代器实现 *-&gt;++ 运算符。

class Polygon {
  public:
    /* ... */

    struct PolygonIterator {
        using iterator_category = std::input_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = Line;
        using pointer = value_type*;
        using reference = value_type&;
        
        explicit PolygonIterator( Point* ptr ) : m_ptr( ptr ) {}
        reference operator*();
        // ^ Should return Line reference, but I have Point in container?
        PolygonIterator& operator++();
        pointer operator->();
        bool operator==(const PolygonIterator& other);
      private:
        Point* m_ptr;
    };
    PolygonIterator begin() { return PolygonIterator( &_vertices.front() ); }
    PolygonIterator end() { return PolygonIterator( &_vertices.back() ); }

【问题讨论】:

  • 第 1 步:选择一个 iterator category。您可以从“前向迭代器”开始。第 2 步:创建课程。阅读您的类别的要求,并为该课程实施所有要求。
  • @HolyBlackCat 你能详细说明一下吗?我一直在 stackoverflow 上阅读 cppreference 和其他类似问题,但仍然不明白要求的实现应该是什么样子。

标签: c++ iterator containers c++20


【解决方案1】:

再次查看您的代码,满足 前向迭代器 要求会很棘手,因为您实际上是动态生成行。因此我建议创建一个input iterator

operator++ 应该只是增加m_ptr,没什么异常。但是您可能想要存储 std::vector 迭代器而不是指针(然后,如果您为标准容器启用迭代器调试,它将扩展到您的迭代器)。

那么你有两个选择:

  1. 将当前的Line 存储在迭代器中。然后*-&gt; 分别返回一个引用和一个指向它的指针。 ++ 需要在增加指针/迭代器后更新此 Line

  2. 按值从operator*返回Line,并将using reference更改为Line以匹配返回类型。这是不寻常的,但允许输入迭代器。

    然后operator-&gt; 变得棘手。它不能返回一个指针(因为没有 Line 指向),所以它必须按值返回一个帮助器类,该类又将存储一个 Line(再次按值),并重载 -&gt;返回一个指向它的指针。您可能还应该更改 using pointer 以匹配此帮助程序类的类型。

【讨论】:

  • 谢谢!选项 2 正是我一直在寻找的,甚至似乎比我以前使用的性能略好 :) operator-> 并不是最漂亮的,但无论如何我很少使用它,而且它似乎可以直观地工作。在阅读了@Serge Ballesta 在另一个答案中链接的文章后,我认为这个功能可以通过新的范围/视图来实现,但我还不能解决这个问题。
【解决方案2】:

欢迎来到不是容器的容器世界!

从一开始,标准库容器就应该直接包含它们的对象。我可以找到 2 篇关于此的参考文章:

长话短说,您的伪容器只是一个代理容器:对象存在于其他地方,而容器只能为它们构建代理。它并不少见,vector&lt;bool&gt; 就是这样一个容器,它的迭代器只是代理迭代器。简单来说,如果您查看标准库实现的源代码,您会发现算法中时不时特殊处理vector&lt;bool&gt;,因为vector&lt;bool&gt; 迭代器在不能时假装是随机访问迭代器甚至满足转发容器的要求。

这意味着您的迭代器将只是代理迭代器,它们的引用类型将是一个对象(代理)而不是真正的引用。因此,它们只不过是简单的输入迭代器。这对你来说是可以接受的,这就足够了,否则你可以看看 C++20 的 range 库,它应该开始为代理容器和迭代器提供一些支持。

【讨论】:

    猜你喜欢
    • 2013-02-17
    • 1970-01-01
    • 2020-12-08
    • 2015-09-06
    • 2015-02-04
    • 1970-01-01
    • 2016-06-18
    • 2011-06-09
    相关资源
    最近更新 更多