hand 162127 640

C++ Core Guidelines: Rules for Enumerations

The section to enumerations has eight rules. Since C++11, we have scoped enumerations which overcome a lot of the drawbacks of classical enumerations. 

 

hand 162127 640

Enumerations are sets of integer values which behave like a type. Here is the summary of the rules:

As I mentioned in the opening of this post: classical enumerations have a lot of drawbacks. Let me explicitly compare classical (unscoped) enumerations and scoped enumerations (sometimes called strongly-typed enumerations) because this important comparison is not explicitly described in the rules.

Here is a classical enumeration:

enum Colour{
  red,
  blue,
  green
};

 

Here are the drawbacks of the classical enumerations:

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Generic Programming (Templates) with C++": October 2024
  • "Embedded Programming with Modern C++": October 2024
  • "Clean Code: Best Practices for Modern C++": March 2025
  • Do you want to stay informed: Subscribe.

     

    • The enumerators have no scope
    • The enumerators implicitly convert to int
    • The enumerators pollute the global namespace
    • The type of enumerator is not defined. It just has to be big enough to hold the enumerator.

    By using the keyword class or struct, the classical enumeration becomes a scoped enumeration (enum class):

    enum class ColourScoped{
      red,
      blue,
      green
    };
    

     

    Now, you must use the scope operator to access the enumerators: ColourScoped::red. ColourScoped::red will not implicitly convert to int and will not pollute the global namespace. Additionally, the underlying type is per default int. 

    After providing the background information, we can directly jump into the rules.

    Enum.1: Prefer enumerations over macros

    Macros don’t respect scope and have no type. This means you can override a previously set macro that specifies a color.

    // webcolors.h 
    #define RED   0xFF0000
    
    // productinfo.h
    #define RED    0
    
    int webcolor = RED;   // should be 0xFF0000
    

     

    With ColourScoped, this will not happen because you have to use the scope operator: ColourScoped webcolour = ColourScoped::red;

    Enum.2: Use enumerations to represent sets of related named constants

    This rule is quite evident because the enumerators are a set of integers that create a type.  

    Enum.3: Prefer enum classes over “plain” enums

    The enumerators of a scoped enum (enum class) will not automatically convert to int. You have to access them with the scope operator.

    // scopedEnum.cpp
    
    #include <iostream>
    
    enum class ColourScoped{
      red,
      blue,
      green
    };
    
    void useMe(ColourScoped color){
    
      switch(color){
      case ColourScoped::red:
        std::cout << "ColourScoped::red" << std::endl;
        break;
      case ColourScoped::blue:
        std::cout << "ColourScoped::blue" << std::endl;
        break;
      case ColourScoped::green:
        std::cout << "ColourScoped::green" << std::endl;
        break;
      }
    }
    
    int main(){
    
      std::cout <<  static_cast<int>(ColourScoped::red) << std::endl;   // 0
      std::cout <<  static_cast<int>(ColourScoped::red) << std::endl;   // 0
    
      std::cout << std::endl;
    
      ColourScoped colour{ColourScoped::red};
      useMe(colour);                                                     // ColourScoped::red
    
    }
    

     

    Enum.4: Define operations on enumerations for safe and simple use

    The rules define an enumeration Day that supports the increment operation.

    enum Day { mon, tue, wed, thu, fri, sat, sun };
    
    Day& operator++(Day& d)
    {
        return d = (d == Day::sun) ? Day::mon : static_cast<Day>(static_cast<int>(d)+1);
    }
    
    Day today = Day::sat;
    Day tomorrow = ++today;
    

     

    The static_cast is necessary in this example because applying the increment operator inside the increment operator would cause an infinite recursion:

    Day& operator++(Day& d)
    {
        return d = (d == Day::sun) ? Day::mon : Day{++d};    // error
    }
    

     

    Enum.5: Don’t use ALL_CAPS for enumerators

    If you use ALL_CAPS for enumerators, you may get a conflict with macros because they are typically written in ALL_CAPS.

    #define RED 0xFF0000
    
    enum class ColourScoped{ RED };  // error
    

    Enum.6: Avoid unnamed enumerations

    If you can’t find a name for the enumerations, the enumerations maybe not be related. In this case, you should use a constexpr value.

    // bad
    enum { red = 0xFF0000, scale = 4, is_signed = 1 };
    
    // good
    constexpr int red = 0xFF0000;
    constexpr short scale = 4;
    constexpr bool is_signed = true;
    

    Enum.7: Specify the underlying type of an enumeration only when necessary

    Since C++11, you can specify the underlying type of enumeration and save memory. Per default, the type of a scoped enum is int and, therefore, you can forward declare an enum.

    // typeEnum.cpp
    
    #include <iostream>
    
    enum class Colour1{
      red,
      blue,
      green
    };
     
    enum struct Colour2: char {
      red,
      blue,
      green
    };
    
    int main(){
    
      std::cout << sizeof(Colour1) << std::endl;  // 4
      std::cout << sizeof(Colour2) << std::endl;  // 1
    
    }
    

    Enum.8: Specify enumerator values only when necessary

    By specifying the enumerator values, you may set a value twice. The following enumeration Col2 has this issue.

    enum class Col1 { red, yellow, blue };
    enum class Col2 { red = 1, yellow = 2, blue = 2 };    // typo
    enum class Month { jan = 1, feb, mar, apr, may, jun,
                       jul, august, sep, oct, nov, dec }; // starting with 1 is conventional
    

     

    What’s next?

    I made it relatively short in this post. The meta-rule you should keep in mind is: use scoped enums.

    The next section of the C++ core guidelines deals with about 35 rules for resource management. This means we dive into the next post right into the heart of C++.

     

     

     

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery,and Matt Godbolt.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Modernes C++ GmbH

    Modernes C++ Mentoring (English)

    Do you want to stay informed about my mentoring programs? Subscribe Here

    Rainer Grimm
    Yalovastraße 20
    72108 Rottenburg

    Mobil: +49 176 5506 5086
    Mail: schulung@ModernesCpp.de
    Mentoring: www.ModernesCpp.org

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

    Your email address will not be published. Required fields are marked *