我有一个班级,例如Point
:
class Point {
public:
double x, y;
};
我有一些容器,例如PointCloud
,它包含在std::vector<Point*>
内:
class PointCloud {
public:
typedef std::vector<Point*>::const_iterator iterator;
iterator begin();
iterator end();
private:
std::vector<Point*> _point
};
现在我可以这样使用我的课程:
PointCloud cloud;
for(auto point : cloud) {
std::cout << point->x << std::endl;
}
我想实现begin()
和end()
所以我的迭代器返回引用而不是指针,我可以这样编写代码:
PointCloud cloud;
for (auto point : cloud) {
std::cout << point.x << std::endl;
}
注意:运营商.
代替->
运营商
我有理由做这件事:
兼容性:首先我用std::vector<Point> _point
写我的课程,如果我将来改变主意并用std::vector<Point*> _point
替换它,我班级的用户不需要改变他们的代码中的东西。
使用参考资料更安全。
无需输入->
代替.
。
我使用引用而不是指针。
这是正确的做法吗?如果是,那么实施它的最佳方式是什么?
最后我找到了正确的解决方案: 提高:: indirect_iterator http://www.boost.org/doc/libs/1_57_0/libs/iterator/doc/indirect_iterator.html
#include <memory>
#include <vector>
#include <iostream>
#include <boost/iterator/indirect_iterator.hpp>
class Point {
public:
Point(double x, double y) : x(x), y(y) {};
double x, y;
};
class PointCloud {
public:
using point_vector = std::vector<std::unique_ptr<Point>>;
using const_iterator = boost::indirect_iterator<point_vector::const_iterator>;
const_iterator begin() { return _points.begin(); }
const_iterator end() { return _points.end(); }
void insert(double x, double y) { _points.emplace_back(new Point(x, y)); }
private:
point_vector _points;
};
int main()
{
PointCloud points;
points.insert(2, 3);
points.insert(4, 5);
for (auto &point : points)
std::cout << point.x << ' ' << point.y << std::endl;
return 0;
}
答案 0 :(得分:4)
假设您可以访问要公开的左值,那么通用deref_iterator<It>
相当容易创建,尽管它有点像打字练习。要覆盖所有迭代器类别,您需要通过所有操作,显然,除了访问元素的任何内容,即运算符*
,->
和[]
。除了操作之外,您还需要提供相应的迭代器特征。我不认为有一个简单的伎俩可以避免这种情况。
相应的迭代器可能看起来像这样(它没有编译,几乎肯定缺少一些操作):
template <typename It>
class deref_iterator {
It it;
using traits = std::iterator_traits<It>;
using base_value = typename traits::value_type;
public:
using value_type = std::decay_t<decltype(*std::declval<base_value>())>;
using reference = value_type&;
using pointer = value_type*;
using difference_type = typename traits::difference_type;
using iterator_category = typename traits::iterator_category;
deref_iterator(): it() {}
explicit deref_iterator(It it): it(it) {}
auto operator*() -> reference { return **this->it; }
auto operator->() -> pointer { return &**this->it; }
auto operator[](difference_type index) -> reference { return *this->it[index]; }
auto operator++() -> deref_iterator& { ++this->it; return *this; }
auto operator++(int) -> deref_iterator { auto rc(*this); ++this->it; return rc; }
bool operator== (deref_iterator const& other) const { return this->it == other.it; }
bool operator!= (deref_iterator const& other) const { return !(*this == other); }
// more in the same spirit for bidirectional and andom access iterator
};
我认为上述内容应该足以应对前向迭代。获得更强大的迭代器只是类似的代码。
值得注意的是,任何影响序列的muations都不会移动指针而是指向值。也就是说,序列变异不适用于这样的迭代器,因为值会被切片(假设有可访问的复制操作;如果它们不可用,则会出现编译时错误)。根本问题是STL在给定位置只考虑一个对象,而在概念上,至少有两个:
对于值类型,这两个实体是相同的,尽管一个值在概念上仍然扮演两个角色。对于类似于解除引用的实体的序列,值是值类型(例如Point
),而保持实体是指向那些实体的对象(例如Point*
)。如果必要时可以区分这些,那么STL可能会更强大。
答案 1 :(得分:0)
如果您的问题仅适用于迭代,那么您只是在讨论循环和打印。
为什么没有两个重载,一个是vector<Point*>
而另一个vector<Point>
。
template<Func&& f>
void LoopThrough(std::vector<Point*> const& v, Func&& f)
{
for (auto p : v)
{
f(p);
}
}
template<Func&& f>
void LoopThrough(std::vector<Point> const& v, Func&& f)
{
for (auto p : v)
{
f(p);
}
}
然后根据需要不断更改typedef。
class PointCloud {
public:
typedef std::vector<Point*>::const_iterator iterator;
// typedef std::vector<Point>::const_iterator iterator;
typedef std::vector<Point*> Vec;
//typedef std::vector<Point> Vec;
iterator begin()
{
return _point.begin();
}
iterator end()
{
return _point.end();
}
Vec& GetStore()
{
return _point;
}
const Vec& GetStore() const
{
return _point;
}
std::vector<Point> _point;
};
您可以使用它,如下所示。
PointCloud p;
p._point.push_back(new Point{ 1.0, 2.0 });
p._point.push_back(new Point{ 3.0, 2.0 });
p._point.push_back(new Point{ 5.0, 2.0 });
p._point.push_back(new Point{ 4.0, 2.0 });
LoopThrough(p._point, [](Point* p) { std::cout << p->x << "\t" << p->y << "\n"; });