Question

I'm currently using a third-party library which contains a class that only provides indexed lookup, i.e. operator[].

I'd like to do a range-based for loop on this class's contents. However, having never written an iterator or iterator adapter, I'm quite lost. It seems that writing iterators is not a straightforward task.

My desired usage is:

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

Instead of having to write:

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

How can this be achieved? Does Boost provide this functionality?

Was it helpful?

Solution

Writing iterators is actually a rather straightforward task, but it gets extremely tedious. Since your container supports indexing by an integer, I assume its iterators would fall into the random access iterator category (if they existed). That needs a lot of boilerplate!

However, to support the range-based for loop, all you'll need is a forward iterator. We'll write a simple wrapper for the container that implements the forward iterator requirements, and then write two functions Iterator begin(Container&) and Iterator end(Container&) that enable the container to be used in the range-based for loop.

This Iterator will contain a reference to the container, the size of the container, and the current index within that container:

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_;
};

Inheriting from std::iterator conveniently declares the appropriate typedefs for use with std::iterator_traits.

Then, you would define begin and end as follows:

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

Switch out Container for whatever the type of container is in your example, and with that, your desired usage works!

The requirements and behavior of all the various kinds of iterators can be found in the tables of section 24.2.2 of the standard, which are mirrored at cppreference.com here.

A random-access iterator implementation of the above, along with a demo of usage with a simple vector_view class can be found live on Coliru or ideone.com.

OTHER TIPS

You can do the following:

1) define your own iterator It that contains, inside, a ref ref to your container container and an index i. When the iterator is dereferenced, it returns ref[i] by reference. You can write it yourself or you can use boost for help, it has an iterator library to easily define your own iterators. Constructor should accept a container& and a size_t. You can make also the const_iterator if this concept applies.

2) When operator++ is invoked on one iterator, it simply does ++i on the internal member. operator== and operator!= should simply compare i. You can assert, for security, that both iterators are coherent, that means their refs point to the same object.

3) add begin and end to your container class or, if this is not possible, define a global begin and end that accept your container& c. begin should simply return It(c, 0). end should return It(c, c.size()).

There could be a problem copying the iterators as they contain a reference and some other minor details, but I hope the overall idea is clear and correct.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top