Defining a custom iterator in C++

Iterators are great. They brought simplicity into C++ STL containers and introduced for_each loops. But what if none of the STL containers is the right fit for your problem? Creating a custom container is easy. But should you really give up for_each loops? By defining a custom iterator for your custom container, you can have the best of both worlds!

 

First stop: Defining a custom container

For the sake of simplicity, let’s make a really simple container. It contains three compile-time fixed elements with no accessors or similar. Additionally it has a method that retrieves the container size.

class CustomContainer
{
public:
   CustomContainer() = default;
   ~CustomContainer() = default;

   size_t size() const;
   
private:
   int field1 = 1;
   int field2 = 2;
   int field3 = 3;
   size_t nSize = 3;
};

Easy as that. It’s our goal to iterate over the three given fields.

Declaring an output iterator-class

Again, for the sake of simplicity, we will implement an output_iterator, which requires no possibility to modify the underlaying container. In order to find out mor about different categories of iterators I strongly recommend reading the C++ Reference website.

The (IMHO) easiest way to implement a custom iterator, is to inherit from the abstract std::iterator class and to specify its template parameters. So let’s take a look at them:

template<class Category, class T, class Distance = ptrdiff_t, class Pointer = T*, class Reference = T&>

The only really complicated parameter is the first one: Category. As explained on the C++ reference website liked above, there are different categories of iterators. We’re implementing an output_iterator. Therefore our category will be “std::output_iterator_tag”.
The second parameter defines the class of objects we’re iterating over. In an array this would be the type of objects stored in it. In our case it is simply “int”. The other three parameters are usually insignificant. “Distance” describes the type in which the distance of two elements inside the conainer is measured and is almost exclusivly ptrdiff_t (usually unsigned 32/64bit int). So our resulting class declaration turns out to be:

class iterator : public std::iterator<std::output_iterator_tag, int>

Well that was painless.

Declaring all necessary methods

Let’s take a look inside the C++ reference website, shall we? The following table is copied directly from the given link:

iterators4.png

The methods an output_iterator needs to define, therefore are: Dereferencing, prefix-increment and postfix-increment. So let’s do exactly that:

class iterator : public std::iterator<std::output_iterator_tag, int>
{
public:
    int operator*() const;
    iterator & operator++();
    iterator operator++(int);
};

By the way: In case you’re as surprised as I was, looking at the second definition of the “++”-operator. This is the correct (and AFAIK only?) way of defining a postfix “++”-operator. Hm. Who would have thought that.

Perfect. We declared an absolutely valid output_iterator-class for our custom container. Let’s implement the methods and we’re done.

Implementing the custom iterator

Looking back at our custom container, we somehow need to point to one of three fields. Easiest way is to define an index, which is saved inside the iterator. Additionally the iterator, which is implemented as an inner class of the custom container, needs to access these fields. In Java an inner class has implicit access to its containing outer class. In C++ you need to pass a valid reference to it.
So our current code looks something like this:

class iterator : public std::iterator<std::output_iterator_tag, int>
{
public:
    explicit iterator(CustomContainer & Container, size_t index = 0);
    int operator*() const;
    iterator & operator++();
    iterator operator++(int);
private:
    size_t nIndex = 0;
    CustomContainer & Container;
};

The increment operators simply increment “nIndex” and return a reference to themselves. The dereferencing-operator however is somewhat more interesting.

int CustomContainer::iterator::operator*() const
{
   switch (nIndex)
   {
   case 0:
      return Container.field1;
   case 1:
      return Container.field2;
   case 2:
      return Container.field3;
   default:
      throw std::out_of_range("Out of Range Exception!");
   }
}

I think the above code is self explaining. Now we’re done, aren’t we? Let’s try it out!

CLion outputs the following error:

CppCustomIterators\main.cpp:8:16: error: no matching function for call to ‘begin(CustomContainer&)’

Of course. Like all containers, ours needs a begin() and an end()-method!
That’s their simple implementation:

CustomContainer::iterator CustomContainer::begin()
{
   return CustomContainer::iterator(*this, 0);
}
CustomContainer::iterator CustomContainer::end()
{
   return CustomContainer::iterator(*this, size());
}

But still Microsoft is not happy:

CppCustomIterators/main.cpp:8: undefined reference to `CustomContainer::iterator::operator!=(CustomContainer::iterator const&) const’

An “!=”-operator is required for the loop to be able to check whether the current iterator has reached the end of the container. Well that makes sense, got to give him that.

bool CustomContainer::iterator::operator!=(const iterator & rhs) const
{
   return nIndex != rhs.nIndex;
}

There you go.

#include <stdio.h>
#include "CustomContainer.h"

int main(int argc, const char* argv[])
{
   CustomContainer customContainer;

   for (auto it = customContainer.begin(); it != customContainer.end(); it++)
   {
      printf("Iterators: %d\n", *it);
   }

   for (auto i : customContainer)
   {
      printf("Ranged-for: %d\n", i);
   }

   getchar();
}

Unbenannt

Everything works as expected.

I hope this simple article helps someone, who’s had a hard time understanding custom iterators. As always the whole code can be found on my GitHub.

Thanks for reading!

3 thoughts on “Defining a custom iterator in C++

  1. Your declaration of post-increment operator is wrong, because this method must return copy of value, but increments it inside. So it can’t return reference!
    Correct impl is:

    iterator& operator++()
    {
    ++nIndex;
    return *this;
    }

    iterator operator++(int)
    {
    iterator tmp(*this);
    operator++();
    return tmp;
    }

    Liked by 1 person

  2. Kudos! Your article has helped me. It is simple and to the point.

    Even though as I learn inheriting from iterator is deprecated.

    Like

Leave a comment