使用基于范围的for循环与第三方容器

时间:2014-04-02 14:00:36

标签: c++ c++11 foreach iterator boost-iterators

我目前正在使用第三方库,其中包含仅提供索引查找的类,即operator[]

我想在这个课程的内容上做一个基于范围的for循环。但是,从未编写过迭代器或迭代器适配器,我很遗憾。似乎编写迭代器并不是一项简单的任务。

我想要的用法是:

for(auto element : container)
{
    ...
}

而不是写:

for(int i = 0; i < container.size(); ++i)
{
    auto element = container[i];
    ...
}

如何实现这一目标? Boost是否提供此功能?

2 个答案:

答案 0 :(得分:3)

编写迭代器实际上是一项相当简单的任务,但它变得非常繁琐。由于您的容器支持整数索引,我假设它的迭代器将落入随机访问迭代器类别(如果它们存在)。这需要很多样板!

但是,为了支持基于范围的for循环,您只需要一个前向迭代器。我们将为实现前向迭代器要求的容器编写一个简单的包装器,然后编写两个函数Iterator begin(Container&)Iterator end(Container&),使容器能够在基于范围的for循环中使用。

Iterator将包含对容器的引用,容器的大小以及该容器中的当前索引:

template<template<typename> class C, typename T>
class indexer : public std::iterator<std::forward_iterator, T>
{
public:
    indexer(C<T>& c, std::size_t i, std::size_t end)
        : c_(std::addressof(c)), i_(i), end_(end) {}

    T& operator*() const {
        return c_[i_];
    }

    indexer<C, T>& operator++() {
        ++i_;
        return *this;
    }
    indexer<C, T> operator++(int) {
        auto&& tmp = *this;
        operator++();
        return tmp;
    }

    bool operator==(indexer<C, T> const& other) const {
        return i_ == other.i_;
    }
    bool operator!=(indexer<C, T> const& other) const {
        return !(*this == other);
    }

private:
    C<T>* c_;
    std::size_t i_, end_;
};

std::iterator继承可以方便地声明相应的typedef,以便与std::iterator_traits一起使用。

然后,您可以按如下方式定义beginend

template<typename T>
indexer<Container, T> begin(Container<T>& c) {
    return indexer<Container, T>(c, 0, c.size());
}

template<typename T>
indexer<Container, T> end(Container<T>& c) {
    auto size = c.size();
    return indexer<Container, T>(c, size, size);
}

根据您的示例中的Container类型切换container,然后,您所需的使用情况有效!

所有各种迭代器的要求和行为可以在标准的第24.2.2节的表格中找到,这些表格在cppreference.com here处镜像。

上面的随机访问迭代器实现,以及简单vector_view类的使用演示,可以在Coliruideone.com上找到。

答案 1 :(得分:1)

您可以执行以下操作:

1)定义您自己的迭代器It,其内部包含对容器ref的引用container和索引i。取消引用迭代器后,它将通过引用返回ref[i]。你可以自己编写它,或者你可以使用boost来获得帮助,它有一个迭代器库来轻松定义你自己的迭代器。构造函数应该接受container&size_t。如果这个概念适用,你也可以const_iterator

2)当在一个迭代器上调用operator++时,它只在内部成员上执行++ioperator==operator!=应该只是比较i。出于安全考虑,您可以assert两个迭代器是连贯的,这意味着它们ref指向同一个对象。

3)将beginend添加到您的容器类中,或者,如果无法这样做,请定义接受begin的全局endcontainer& c }。 begin应该只返回It(c, 0)end应该返回It(c, c.size())

复制迭代器可能会出现问题,因为它们包含引用和其他一些细节,但我希望整体思路清晰正确。