Class vs enum class as an index type

Viewed 269

P0138R2 proposal begins with1

There is an incredibly useful technique for introducing a new integer type that is almost an exact copy, yet distinct type in modern C++11 programs: an enum class with an explicitly specified underlying type. Example:

enum class Index : int { };    // Note: no enumerator.

One can use Index as a new distinct integer type, it has no implicit conversion to anything (good!).

To convert Index to its underlying type it is useful to define

int operator*(Index index) {
    return static_cast<int>(index);
}

Another way to create Index type is to use old class:

class Index final {
public:
     explicit Index(int index = 0) : index_(index) { }

     int operator*() const {
         return index_;
     }

private:  
     int index_;
};

Both seem to be largely equivalent and can be used in the same way:

void bar(Index index) {
    std::cout << *index;
}

bar(Index{1});

int i = 1;
bar(Index{i});

Pro of enum class: the comparison operators are defined automatically, con of enum class: index value for the default constructed enum class can't be specified, it is always zero.

Are there other practical differences between these alternatives?


1 I changed uint32_t to int to avoid #include <cstdint>.

1 Answers

The alternative for strong types I use is a variation on NamedTypes by Jonathan Boccara. He nicely explains all of the details in multiple posts on his blog, see https://www.fluentcpp.com/2016/12/08/strong-types-for-strong-interfaces/

It's slightly more verbose in writing: using Index = NamedType<int, struct IndexTag, Comparable, ImplicitlyConvertibleTo<int>>;

When you construct this, you need to write something like Index{0}, though, the moment you use it as an index, it should automatically convert to the underlying type.

It has several advantages, including the ability to work on any type. The biggest disadvantage is that it's an external library you have to import instead of built-in functionality.

Related