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?
uint32_t
to int
to avoid #include <cstdint>
.
In C++, I can define a enum and use the enum as the array index.
Difference between Enums and ClassesThe only difference is that enum constants are public , static and final (unchangeable - cannot be overridden). An enum cannot be used to create objects, and it cannot extend other classes (but it can implement interfaces).
enum class is not a class definition - the combination of keywords is used to define a scoped enumeration, which is a completely separate entity from a class .
The enum value BAD resides in the general identifier scope, while the class type BAR resides in the class identifier scope. That is the reason why you are allowed to have both an enum value and a class with the same name: both names do not collide.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With