9

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:

  1. If you convert to scoped enum, move it out of classes and into global scope or namespace scope (to improve usability and avoid the latter usage example above).
  2. If you keep the enum unscoped, make sure it's in namespace/class/function/etc scope so that it doesn't conflict with other names.

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?

void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • 2
    The advantage of switching to `enum class` is everything you can read about why it was added; the disadvantage of switching to `enum class` is everything you wrote above. That doesn't tell us what to do when writing our code from scratch, though. – Lightness Races in Orbit Dec 05 '14 at 16:40
  • 1
    What's the problem with the second usage? – Daniel Dec 05 '14 at 16:40
  • 1
    @Daniel Verbosity. It's like using 2 nested namespaces for your objects when only 1 would suffice. – void.pointer Dec 05 '14 at 16:41
  • "just keep enumerations already in classes as unscoped enums" - that's sometimes sensible, and sometimes not. What if you want two enumerations in the same class? – Mike Seymour Dec 05 '14 at 16:41
  • @MikeSeymour I think the issue (at least for our legacy code base) is that enums most times were added to classes to simulate what scoped enumerations provide today, so we could do `Class::Enumerator`. So, when all that changes is the addition of the `class` keyword, things become *too* verbose for no real reason: `Class::Enumeration::Enumerator` – void.pointer Dec 05 '14 at 16:44
  • scoped enums is creaed for type-safe, personally i don't like it. old enum is much better atleast you don't need `static-cast` any where – MORTAL Dec 05 '14 at 16:48
  • 3
    @MORTAL: If you're `static_cast`ing your scoped enums then you missed the point entirely and your code is wrong. – Lightness Races in Orbit Dec 05 '14 at 17:34
  • The reason you can't find anything saying they're deprecated is because they're not deprecated, and that's because they're still useful. Scoped enumerations are different things, and useful in different places, they definitely don't replace all uses of unscoped ones. – Jonathan Wakely Dec 05 '14 at 17:50

1 Answers1

11

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 enums 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 enums -- a list of distinguished values, whose underlying type and value is merely an implementation detail.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524