Dziwi mnie, że nikt nie wspomniał o enumach z zasięgiem które są bezpieczniejsze w użyciu. Enumy pokazane przez kolegów wyżej, są enumami bez zasięgu. Co to oznacza? Weźmy na warsztat takiego enuma:
enum VALUES { VALUE1, VALUE2 };
To jest enum bez zasięgu, oznacza to, że nie musimy odwoływać się do konkrentego enuma, by dokopać się do jego wartości.
std::cout << static_cast < int >( VALUE1 ) << std::endl;
Ten kod wyświetli wartość pola VALUE1. Enumy, jeśli nie nadamy wartości ich polom, same nadadzą domyślne wartości od zera, więc w tym wypadku wartość pola VALUE1 będzie równa 0, a wartość pola VALUE2 będzie równa 1. Na pierwszy rzut oka, enumy bez zasięgu nie wydają się problematyczne, problem pojawi się w takim kodzie:
#include <iostream>
char VALUE = 64;
enum VALUES { VALUE = 63 };
int main()
{
std::cout << static_cast < int >( VALUE ) << std::endl;
return 0;
}
Taki kod się nie skompiluje, bo inaczej wystąpiła by redeklaracja zmiennej VALUE. Odwołanie się do VALUE jest dwuznaczne, właśnie przez to, że enum VALUES jest bez zasięgu.
prog.cpp:4:24: error: 'VALUE' redeclared as different kind of symbol
enum VALUES { VALUE = 63 };
^
prog.cpp:3:6: note: previous declaration 'char VALUE'
char VALUE = 64;
To jak wyglądają enumy z zasięgiem? Wystarczy po enum wstawić słowo kluczowe
class
, lub
struct
.
enum class VALUES { VALUE = 63 };
Jeśli podmienimy naszego enuma bez zasięgu na enuma z zasięgiem, nasz kod się skompiluje i program wyświetli wartość 64.
Do pól takich enumów odwołujemy się jak do statycznych pól klasy. Chcąc wyświetlić pole VALUE z enuma VALUES, musimy się do tego enuma odwołać.
std::cout << static_cast < int >( VALUES::VALUE ) << std::endl;
To samo mogliśmy zrobić również z poprzednim enumem. Dla enumów bez zasięgu operator zasięgu również funkcjonuje jak należy.
Kolejną cechą enuma jest, to że jego wartościami są zmienne
constexpr
. Takie wartości działają tak samo jak zwykłe wartości const, lecz ich wartość jest znana w chwili kompilacji. Takie mogą być wykorzystane tam gdzie operacje przeprowadza się w momencie kompilacji, czyli np. w szablonach. Pole enuma może również przybrać wartość zwracaną przez funkcję, lecz zwracana wartość musi być znana w chwili kompilacji, więc co za tym idzie, musi być to wartość
constexpr
Kolejną bardzo ciekawą rzeczą jest to, że możemy zdefiniować jakiego typu będą pola enuma. Nie znaczy to, że każde osobne pole może być innego typu, a jest to typ określany dla każdego pola. Nie może być to każdy typ, musi być to typ całkowity, czyli np. char, short, long, long long i ich odpowiedniki nie uwzględniające wartości mniejszych od 0.
enum class KEY
: char
{ BACKSPACE = 10, TAB = 11, ESCAPE = 33 };