自定义容器的迭代器

问题描述

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

Point,表示一个二维 int 坐标

struct Point {
  int x;
  int y;
}

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

class Line {
    Point start;
    Point end;
}

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

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 解决方案。


我意识到这个问题过于含糊,更具体地说,我是在问如何为我的迭代器实现 *->++ 运算符。

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

解决方法

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

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

那么你有两个选择:

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

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

    然后 operator-> 变得棘手。它不能返回一个指针(因为没有 Line 指向),所以它必须按值返回一个辅助类,它反过来会存储一个 Line(再次按值),并且重载 -> 以返回指向它的指针。您可能还应该更改 using pointer 以匹配此辅助类的类型。

,

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

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

长话短说,你的伪容器只是一个代理容器:对象存在于别处,而容器只能为它们构建代理。它并不少见,vector<bool> 就是这样一个容器,它的迭代器只是代理迭代器。只需查看标准库实现的源代码,您就会发现算法中的 vector<bool> 时不时特殊处理,因为 vector<bool> 迭代器假装是随机访问迭代器甚至无法满足转发容器的要求。

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