Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a C++ enum class enumerator as std::array index without an explicit cast

I want to use a C++ enum class as std:array index without calling an explicit cast, when I want to refer to a specific index.

I furthermore use a typedef for a fixed sized std::array.

typedef std::array<int, 3> MyType;
enum class MyEnum {
  ENUMERATOR0 = 0,
  ENUMERATOR1 = 1,
  ENUMERATOR2 = 2,
};

So instead of using:

MyType my_type = {0};
my_type[static_cast<int>(MyEnum::ENUMERATOR0)] = 42;

I want to use:

my_type[MyEnum::ENUMERATOR0] = 42;

Therefore, I assume it is required to overload the subscript operator of my MyType (std::array) type. However, I couldn't figure out, how to overload the subscript operator in my case. For simplicity reasons, I would like to avoid using a class instead of the typedef. How can I do this?

like image 885
ltsstar Avatar asked Dec 25 '17 22:12

ltsstar


People also ask

Can I use enum as array index?

In C++, I can define a enum and use the enum as the array index.

Can you index an enum in C?

You can't do that. A C enum is not much more than a bunch of constants. There's no type-safety or reflection that you might get in a C# or Java enum . Save this answer.

Can we use enum as array?

Enums are value types (usually Int32). Like any integer value, you can access an array with their values. Enum values are ordered starting with zero, based on their textual order. MessageType We see the MessageType enum, which is a series of int values you can access with strongly-typed named constants.

Should enum class be pass by value or reference?

enum class holds an integral value just like a regular enum so you can safely pass it by value without any overhead. Notice that compiler may sometimes optimize pass by reference as well by replacing it with pass by value. But passing by reference may result in some overhead when such an optimization is not applied.


1 Answers

I found a good solution for this. You can both use enum classes as indices in your arrays and also get the added benefit of ensuring type safety (i.e. preventing use of the wrong enum type as indices) by subclassing std::array and overriding its operator[] method.

Here is an example.

You can define enum_array like this:

#include <array>

// this is a new kind of array which accepts and requires its indices to be enums
template<typename E, class T, std::size_t N>
class enum_array : public std::array<T, N> {
public:
    T & operator[] (E e) {
        return std::array<T, N>::operator[]((std::size_t)e);
    }

    const T & operator[] (E e) const {
        return std::array<T, N>::operator[]((std::size_t)e);
    }
};

And you can use it like this:

int main() {
    enum class Fruit : unsigned int {
        Apple,
        Kiwi
    };

    enum class Vegetable : unsigned int {
        Carrot,
        Potato
    };

    // Old way:
    std::array<int, 3> old_fruits;
    std::array<int, 3> old_veggies;

    old_fruits[(int)Fruit::Apple] = 3;          // compiles but "ugly"
    old_veggies[(int)Vegetable::Potato] = 7;    // compiles but "ugly"

    old_fruits[(int)Vegetable::Potato] = 3;     // compiles but shouldn't compile!
    old_fruits[2] = 6;                          // compiles but may or may not be desirable

    // New way:
    enum_array<Fruit, int, 3> fruits;
    enum_array<Vegetable, int, 3> veggies;

    fruits[Fruit::Apple] = 3;
    veggies[Vegetable::Potato] = 7;

    // fruits[Vegetable::Potato] = 3;   // doesn't compile :)
    // fruits[2] = 6;                   // doesn't compile
    // fruits[(int)Fruit::Apple] = 3;   // doesn't compile
} 
like image 152
jordi Avatar answered Sep 22 '22 09:09

jordi