Iterating over enums in C++

In this article I want to share a simple and obvious trick in C++, which, despite its simplicity and obviousness, as it turns out, not everyone knows about.

Let's assume you have enum class, and you want to iterate through its elements, that is, call some function for each of the values ​​of this enum. This can be useful, well… as an example, if the enum elements represent parameters (keys for values) that you can receive from some entity or service, and you need to process them all at once in turn, for example, for serialization or logging.

There is a method in C# for this task Enum.GetValues(typeof(T)), which returns a collection of all values ​​of this enum. There is no such method in C++, but we can try to implement something similar.

So, let’s take the usual and everyone’s favorite range-based for loop:

for (auto i : some)
{
  ...
}

Cppreference tells usthat to use a range-based for loop with any type that is not an array, that type must have begin() and end() methods that return something that supports the operators ++ (to move to the next value) and * (to get a value), that is, an iterator.

Based on this, we proceed to create our template class into which we can wrap any enum. First, let's start with the iterator:

template <typename T>
class Enum
{
public:
  class Iterator
  {
  public:
    constexpr explicit Iterator(std::underlying_type_t<T> value)
      : m_value(value)
    {}
    constexpr T operator*() const
    {
      return static_cast<T>(m_value);
    }
    constexpr void operator++()
    {
      ++m_value;
    }
    constexpr bool operator!=(Iterator rhs) const
    {
      return m_value != rhs.m_value;
    }
  private:
    std::underlying_type_t<T> m_value;
  };
};

Enum “under the hood” is a numeric value, so we can easily convert it to a numeric type and back (it won't necessarily be an int, so we use std::underlying_type). The only condition is that the elements in the enum must be sequential, without gaps.

We've figured out the iterator, but how to implement the begin() and end() functions?

To do this, we will need to play a little trick on our enam itself. Let's say you want to create an enumeration with elements One, Two and Three.

Let's declare it like this:

enum class MyType
{
  Begin,
  One = Begin,
  Two,
  Three,
  End
};

Now we can easily access the first element of the enumeration through the alias MyType::Begin (which is numerically equivalent to element One), and also check whether we have reached the end of the enumeration by comparing with MyType::End.

After this, the code for the begin() and end() methods fits in just a couple of lines:

template <typename T>
constexpr typename Enum<T>::Iterator begin(Enum<T>)
{
  return typename Enum<T>::Iterator(static_cast<std::underlying_type_t<T>>(T::Begin));
}

template <typename T>
constexpr typename Enum<T>::Iterator end(Enum<T>)
{
  return typename Enum<T>::Iterator(static_cast<std::underlying_type_t<T>>(T::End));
}

And we can easily iterate over all the elements of any enum containing a Begin and an End using our wrapper:

for (auto item : Enum<MyType>)
{
  // используем item:
  // код здесь внутри будет вызван три раза: для One, Two и Three
}

And as a bonus, you can add the size() method for our Enum class, in case it comes in handy:

static constexpr std::size_t size()
{
  return static_cast<std::underlying_type_t<T>>(T::End) - static_cast<std::underlying_type_t<T>>(T::Begin);
}

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *