enumClassic

Strongly-Typed Enums

Enumerations are a convenient way to define integer constants with names. These integer constants are called enumerators. Sadly, classical enums have a few drawbacks.

The drawbacks of enumerations in classical C++

 A short reminder. Three drawbacks of enumerations.

  1. The enumerators implicitly convert to int.
  2. They introduce the enumerators in the enclosing scope.
  3. The type of enumeration can not be specified.

First to point 3: Enumerations can not be forward declared because their type is not known. There is only a guarantee for the enumerators in classical C++. The type must be integral and big enough to hold the enumerators.

Point 1 and point 2 are more surprising.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// enumClassic.cpp

#include <iostream>

int main(){
	
  std::cout << std::endl;
	
  enum Colour{red= 0,green= 2,blue};
  
  std::cout << "red: " << red << std::endl;
  std::cout << "green: " << green << std::endl;
  std::cout << "blue: " << blue << std::endl;
  
  int red2= red;
  
  std::cout << "red2: " << red2 << std::endl;
  
  // int red= 5;  ERROR
  
}

On the one hand are the enumerators red, green, and blue, known in the enclosing scope. Therefore, the definition of the red variable in line 19 is impossible. On the other hand, red can be implicitly converted to int.

enumClassic

If you use no name for an enumeration like enum{red, green, blue},  the enumerators will be introduced in the enclosing scope.

But that surprise ends with C++11.

 

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.

     

    Strongly-typed enumerations

    The strongly-typed enumerations have to follow stronger rules:

    1. The enumerators can only be accessed in the scope of the enumeration.
    2. The enumerators don’t implicitly convert to int.
    3. The enumerators aren’t imported in the enclosing scope.
    4. The type of enumerators is, by default, int. Therefore, you can forward the enumeration.

    The syntactical difference between the classic enumerations and the strongly-typed enumerations is minimal. The strongly-typed enumerations additionally get the keyword class or struct.

    enumClassicStrongEng

    If you want to use an enumerator as an int, you must explicitly convert it with static_cast.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // enumCast.cpp
    
    #include <iostream>
    
    enum OldEnum{
      one= 1,
      ten=10,
      hundred=100,
      thousand= 1000
    };
    
    enum struct NewEnum{
      one= 1,
      ten=10,
      hundred=100,
      thousand= 1000
    };
    
    int main(){
    	
      std::cout << std::endl;
    
      std::cout << "C++11= " << 2*thousand + 0*hundred + 1*ten + 1*one << std::endl;
      std::cout << "C++11= " << 2*static_cast<int>(NewEnum::thousand) + 
                                0*static_cast<int>(NewEnum::hundred) + 
                                1*static_cast<int>(NewEnum::ten) + 
    	                    1*static_cast<int>(NewEnum::one) << std::endl;
    
    }
    

     

    To calculate or output the enumerators, you must convert them into integral types. Either the addition or the output of strongly-typed enumerations is defined. 

    enumCast 

     

    I often speak in this post about classical versus strongly-typed enumerations. Often there are called scoped and unscoped enumerations.

    Explicitly specifying the type

    I ignored one feature of the enumerations in C++11. You can explicitly specify the type of enumerators. By default, it’s int.

    But that does not have to be. You can use integral types like bool, char, short int, long int, or, long long int.  Read msdn.microsoft.com for the details. You can read in my post, Check types how you can check at compile time if a type is integral. 

    You can independently use the scoped property and the explicit type specification of an enumeration. Dependent on the base types, the enumerations have different sizes.

     

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    // enumType.cpp
    
    #include <iostream>
    #include <climits>
    
    enum struct Colour0: bool{
      red,     // 0
      blue     // 1
    };
    
    enum Colour1{
      red= -5,   
      blue,      // -4
      green      // -3
    };
    
    enum struct Colour2: char{
      red= 100,
      blue, // 101
      green // 102
    };
    
    enum class Colour3: long long int{
      //red= 	std::numeric_limits<long long int>::min();
      red= LLONG_MIN,
      blue,    // std::numeric_limits<long long int>::min() + 1
      green    // std::numeric_limits<long long int>::min() + 2
    };
    
    int main(){
    
      std::cout << std::endl;
    
      std::cout << "sizeof(Colour0)= "  << sizeof(Colour0) << std::endl;
      std::cout << "sizeof(Colour1)= "  << sizeof(Colour1) << std::endl;
      std::cout << "sizeof(Colour2)= "  << sizeof(Colour2) << std::endl;
      std::cout << "sizeof(Colour3)= "  << sizeof(Colour3) << std::endl;
      
      std::cout << std::endl;
    
      std::cout << "Colour0::red: " << static_cast<bool>(Colour0::red) << std::endl;
      std::cout << "red: " << red << std::endl;
      std::cout << "Colour2::red: " << static_cast<char>(Colour2::red) << std::endl;
      std::cout << "Colour3::red: " << static_cast<long long int>(Colour3::red) << std::endl;
    
    }
    

     

    My in Microsoft Visual Studio 12.0 included C++ compiler cl.exe can not evaluate the expression std::numeric_limits<long long int>::min() (line 24) at compile time. According to the C++11 standard, is std::numeric_limits<long long int>::min() a constant expression. Therefore, I can use this expression to initialize an enumerator. Because of the missing feature in cl.exe, I have to use the macro LLONG_MIN in line 25. This macro is defined in the same header as the expression std::numeric_limits: <climits>.

     At the end, the output.

     enumType

    What’s next?

    Typically, you have in the embedded world a system of systems. Or, to say it differently: Many autonomous systems interact with each other to build the whole system. If I change the term autonomous system with the object, we are in the domain of object-oriented programming. From my perspective, object-oriented abstraction is an abstraction with significant added value for a deeper understanding of embedded systems. Therefore, I will write in the next post about the new keywords override and final, which empowers you to manage the object hierarchies.

     

     

     

     

    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, Matt Godbolt, and Honey Sukesan.

    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 *