包装STL迭代器以使用引用而不是指针

时间:2014-12-26 20:32:31

标签: c++ c++11 stl

我有一个班级,例如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;
}  

注意:运营商.代替->运营商

我有理由做这件事:

  1. 兼容性:首先我用std::vector<Point> _point写我的课程,如果我将来改变主意并用std::vector<Point*> _point替换它,我班级的用户不需要改变他们的代码中的东西。

  2. 使用参考资料更安全。

  3. 无需输入->代替.

  4. 我使用引用而不是指针。

    这是正确的做法吗?如果是,那么实施它的最佳方式是什么?

    最后我找到了正确的解决方案: 提高:: 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;
    }
    

2 个答案:

答案 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";  });