Enumerators of scoped enums are visible only within the enum, convert to other types only with a cast, and always support forward-declared because their default underlying type is int.
Scope
Generally speaking, declaring a name inside curly braces limits the visibility of that name to the scope defined by the braces, with one exception: the C++98-style enums, which lead to enumerator names leaking into the scope containing their enum definition, and thus have an official term - unscoped enums.
|
|
As their new C++11 counterparts, by adding a class in declaration, scoped enums don’t leak names:
|
|
Implicit conversion
The fact that scoped enums have strong typed enumerators results in their inability to implicitly convert to integral types (and, from there, to floating-point types), which behavior is otherwise permited in terms of unscoped enums:
|
|
Instead, in order to convert typefrom Color to a different type, we need a cast:
|
|
Forward declaration
Technically speaking, both scoped and unscoped enums may be forward-declared, except that unscoped ones need a bit of additional work - by specifying the underlying type for unscoped enums1:
|
|
Since scoped enums have a default underlying type of int, forward declaration is always supported:
|
|
With the help of forward declaration, the header containing the declarations requires no recompilation if Status’s definition is revised. Furthermore, it is also possible that continueProcessing’s implementation need not be recompiled2.
Twist
There’s still some situation where unscoped enums may be useful: when referring to fields within C++11’s std::tuples. Suppose we have a tuple holding values for the name, email address, and reputation value for a user at a social networking website:
|
|
To get field value, using an unscoped enum to associate names with field numbers may be helpful:
|
|
To mimic the similar behavior, using scoped enums is more verbose:
|
|
To save some typing, we might consider define a helper function, or in a more generalized form, a function template toUType that takes an arbitrary enumerator and return its value as a compile-time constant:
|
|
In C++14, we may simplify the toUType to a sleeker form:
|
|
And then we access a field of the tuple like this:
|
|
Still more to write than use of the unscoped enum, but it also avoids namespace pollution and inadvertent conversions involving enumerators, so those extra characters might still be a reasonable to pay for.
-
Since there is no default underlying type for unscoped
enums, to make it possible for compilers to select an underlying type for eachenumprior to theenumbeing used, C++98 supports onlyenumdefinitions (when all enumerators are listed), whileenumdeclarations are not allowed. ↩︎ -
This happens if
Statusis modified (e.g., add a new enumerator), butcontinueProcessing’s behavior is unaffected (e.g., the function doesn’t use the newly added enumerator). ↩︎