Enumerations in C++
Tags: cpp
Published: 2023-10-22 03:06:46

---
title: Enumerations in C++
published: true
description: Best practices for using enumerations in C++.
tags: #cpp
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-10-20 13:57 +0000
---

## Introduction

C++ inherited enumerations from C, warts and all. Everything about [enumerations in C](https://dev.to/pauljlucas/enumerations-in-c-ae7) also applies in enumerations C++ (so if you haven’t read that article, you should).  The problems with enumerations in C are:

+ Constants are not scoped; instead, they are “injected” into the surrounding scope.  Sometimes, this causes name collisions.

+ Values implicitly convert to their underlying integral value and vice versa.  While sometimes convenient, this can lead to bugs silently creeping in.

+ Enumerations can not be forward-declared.

[C++11](https://en.wikipedia.org/wiki/C%2B%2B11) extended enumerations to fix all of these problems.

## `enum class`

So as to remain backwards-compatible with C, enumerations weren’t touched. Instead, C++11 added enumeration classes that fix all of the issues with C-style enumerations:

```cpp
enum class color {  // Note "class" keyword.
  BLACK,
  WHITE,
  BLUE,
  GREEN,
  RED,
};
```

Incidentally, `enum struct` can also be used, but there’s no difference.

### Constant Scoping

Enumeration classes immediately fix the scoping problem in that enumeration constants are _not_ “injected” into the surrounding scope.  Instead, they’re referred to via the `::` operator:

```cpp
enum c = color::BLACK;
```

For C-style enumerations, `::` can also be used even though it isn’t necessary.

### Constant Conversion

Unlike C-style enumerations, enumeration class constants do _not_ implicitly convert to their underlying integral values:

```cpp
int n = color::RED;                      // error
```

Instead, an explicit cast is required:

```cpp
int n = static_cast<int>( color::RED );  // OK
```

### Bit Flag Values

If enumeration class constants do not implicitly convert to their underlying integral values, then the bitwise operators don’t work:

```cpp
enum class c_int_fmt {
  NONE     = 0,
  SHORT    = 1 << 0,
  INT      = 1 << 1,
  LONG     = 1 << 2,
  UNSIGNED = 1 << 3,
  CONST    = 1 << 4,
  STATIC   = 1 << 5,
};

c_int_fmt f = c_int_fmt::UNSIGNED | c_int_fmt::INT;  // error
```

However, you can overload the bitwise operators:

```cpp
constexpr c_int_fmt operator|( c_int_fmt lhs, c_int_fmt rhs ) {
    using U = std::underlying_type_t<c_int_fmt>;
    return static_cast<c_int_fmt>( static_cast<U>(lhs) | static_cast<U>(rhs) );
}
```

You can overload the other bitwise operators `&`, `^`, `~`, `|=`, `&=`, and `^=` similarly. However, if you agree that it’s rather tedious to overload _seven_ operators for _every_ enumeration class you’re using for bit flag values, you define a macro like:

```cpp
#define ENABLE_BITWISE_OPERATORS_FOR_ENUM(E)                            \
  static_assert( std::is_enum_v<E>, "enumeration type required" );      \
  constexpr E operator|( E lhs, E rhs ) {                               \
    using U = std::underlying_type_t<E>;                                \
    return static_cast<E>( static_cast<U>(lhs) | static_cast<U>(rhs) ); \
  }                                                                     \
  constexpr E operator&( E lhs, E rhs ) {                               \
    using U = std::underlying_type_t<E>;                                \
    return static_cast<E>( static_cast<U>(lhs) & static_cast<U>(rhs) ); \
  }                                                                     \
  constexpr E operator^( E lhs, E rhs ) {                               \
    using U = std::underlying_type_t<E>;                                \
    return static_cast<E>( static_cast<U>(lhs) ^ static_cast<U>(rhs) ); \
  }                                                                     \
  constexpr E operator~( E e ) {                                        \
    using U = std::underlying_type_t<E>;                                \
    return static_cast<E>( ~static_cast<U>( e ) );                      \
  }                                                                     \
  constexpr E operator|=( E &lhs, E rhs ) {                             \
    return (lhs = lhs | rhs);                                           \
  }                                                                     \
  constexpr E operator&=( E &lhs, E rhs ) {                             \
    return (lhs = lhs & rhs);                                           \
  }                                                                     \
  constexpr E operator^=( E &lhs, E rhs ) {                             \
    return (lhs = lhs ^ rhs);                                           \
  }                                                                     \
  using type_to_eat_semicolon = int
```

And then use it like:

```cpp
ENABLE_BITWISE_OPERATORS_FOR_ENUM( c_int_fmt );
```

> The `type_to_eat_semicolon` is a common trick used when you want to use `;` after a use of a macro, but the macro’s definition ends with something that doesn’t allow a `;` to follow it, in this case the closing `}` of a function definition.  The trick works because it’s legal to alias a type via `using` (or `typedef`) multiple times so long as the aliased type is the same.

### Forward Declaration

Enumerations can be forward-declared, but only when the underlying type is specified:

```cpp
enum class color : uint8_t;
```

Forward declaration is also allowed for C-style enumerations so long as the underlying type is specified.

## Older C++ Code

In pre-C++11 code you may encounter, there was a trick to making C-style enumerations not inject their constants into the global scope:

```cpp
namespace color {  // Pre-C++11 trick.
  enum type {
    BLACK,
    WHITE,
    BLUE,
    GREEN,
    RED,
  };
}
```

That is, wrap the enumeration inside a `namespace` with the name you _would_ have given the enumeration and always name the enumeration `type`:

```cpp
color::type c = color::RED;
```

This trick is no longer necessary.  However, it’s still perfectly fine and useful to be able to put enumerations inside either classes or namespaces for the same reasons you’d put anything inside classes or namespaces.

## Nested Enumerations

If you have an enumeration class nested inside either a `class` or `namespace` like:

```cpp
namespace vt100 {
  enum class color {
    BLACK,
    WHITE,
    BLUE,
    GREEN,
    RED,
  };
  // ...
}
```

Then when using it:

```cpp
vt100::color c = vt100::color::RED;  // Verbose.
```

It gets a bit verbose to have to always specify `color` when though you’ve already specified `vt100`.  Starting in C++20, you can “import” enumeration constant names into their enclosing scope:

```cpp
namespace vt100 {
  enum class color {
    BLACK,
    WHITE,
    BLUE,
    GREEN,
    RED,
  };
  using enum color;  // Import constants into vt100 namespace.
  // ...
}
```

Now you can instead do:

```cpp
vt100::color c = vt100::RED;         // Better.
```

## Conclusion

All of the best practices that apply to [enumerations in C](https://dev.to/pauljlucas/enumerations-in-c-ae7) also apply to enumerations in C++.

Additionally, in new C++ code, enumeration classes should be used exclusively, especially for those declared in the global scope so as not to “pollute” the global namespace with all their constant names.  Use C-style enumerations only when compatibility with C is required.