问题描述
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
迭代器而不是一个指针(然后,如果您为标准容器启用迭代器调试,它将扩展到您的迭代器)。
那么你有两个选择:
-
将当前的
Line
存储在迭代器中。然后*
和->
分别返回一个引用和一个指向它的指针。++
将需要在增加指针/迭代器后更新此Line
。 -
按值返回
Line
中的operator*
,并将using reference
更改为Line
以匹配返回类型。这是不常用的,但允许输入迭代器。然后
operator->
变得棘手。它不能返回一个指针(因为没有Line
指向),所以它必须按值返回一个辅助类,它反过来会存储一个Line
(再次按值),并且重载->
以返回指向它的指针。您可能还应该更改using pointer
以匹配此辅助类的类型。
欢迎来到不是容器的容器世界!
从一开始,标准库容器就应该直接包含它们的对象。我可以找到 2 篇关于此的参考文章:
- When Is a Container Not a Container? 作者:Herb Sutter,和
- To Be or Not to Be (an Iterator) 作者:Eric Niebler
长话短说,你的伪容器只是一个代理容器:对象存在于别处,而容器只能为它们构建代理。它并不少见,vector<bool>
就是这样一个容器,它的迭代器只是代理迭代器。只需查看标准库实现的源代码,您就会发现算法中的 vector<bool>
时不时特殊处理,因为 vector<bool>
迭代器假装是随机访问迭代器甚至无法满足转发容器的要求。
这意味着你的迭代器将只是代理迭代器,它们的引用类型将是一个对象(代理)而不是一个真正的引用。因此,它们只不过是简单的输入迭代器。对你来说是可以接受的,这就足够了,否则你可以看看 C++20 的 range
库,它应该开始为代理容器和迭代器提供一些支持。