I haven't found any wording in the C++11 standard that says unscoped enums are deprecated, but from a pragmatic perspective I'm wondering if they are still useful. A lot of people on my team have gotten in the habit of converting unscoped enums to scoped enums, but it's caused some headache:
class foo
{
public:
enum MyEnum { One, Two, Three };
};
They convert this to:
class foo
{
public:
enum class MyEnum { One, Two, Three };
};
Which means when these enumerators are used, instead of foo::One
, it looks like foo::MyEnum::One
. I've been asking the following best-practices:
The main difference between the two points is that for #1 we don't put them in classes, which otherwise adds some verbose indirection.
All of this seems like over complication, and seems like it would be much simpler to just keep enumerations already in classes as unscoped enums. What's the general best-practice approach for deciding between the two?
Enumerations make for clearer and more readable code, particularly when meaningful names are used. The benefits of using enumerations include: Reduces errors caused by transposing or mistyping numbers. Makes it easy to change values in the future.
An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it.
In an unscoped enum, the scope is the surrounding scope; in a scoped enum, the scope is the enum-list itself. In a scoped enum, the list may be empty, which in effect defines a new integral type. class. By using this keyword in the declaration, you specify the enum is scoped, and an identifier must be provided.
Scoped enumerators cannot implicitly convert to their underlying type. If you need your enum values to implicitly convert to their underlying type, you cannot use a scoped enumerator.
An example of when this is useful is when you are talking to an API out of your control, and your enum values are bit flags. The API (you don't control) that expects an uint32_t
or some other integral type as a bit flag.
You can override operator|
etc in order to keep everything "in type", or have them generate the underlying type -- but a single element of your enum class
cannot implicitly convert to uint32_t
.
Another use for unscoped enum
s I find useful is to replace #define FOO 32
style macros. Instead of textual substitution, I get a token that has the same meaning, and I don't have to rewrite the code base. If there are a tightly grouped set of such values, I can eventually reach the point where I can change the int
arguments that are expecting such #define
tokens to be passed with enum
values, and the parameters are now typed!
This allows gradual migration towards a better code base.
The next step might be to use scoped enums for those values, but the overhead of having to do everything at once can mean that the first step may not be taken. The perfect is the enemy of the good.
On the other hand, if your enums are really just a set of enumerated values, and their value in the underlying type is unimportant, then scoped enums are almost always a better idea than unscoped enums. They prevent accidental conversion to the underlying type: if the value in the underlying type is merely an implementation detail, such conversion can cause bugs.
This is by far the most common use case I find for enum
s -- a list of distinguished values, whose underlying type and value is merely an implementation detail.
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