Wyliczenia kwalifikowane

2008-07-01 23:26

W C++ typy wyliczeniowe deklaruje się zwykle poprzez coś podobnego do poniższego kawałka kodu:

  1. enum Sides { Left = -1, Middle = 0, Right = 1 };

Jego skutkiem jest jednak to, że nazwy stałych typu (tutaj: Left, Middle i Right) będą widoczne w całej przestrzeni nazw zawierającej daną deklarację enum. Jeśli więc przypadkiem jest ona globalna, to całkiem łatwo może ona spowodować konflikt chociażby z innym typem w rodzaju:

  1. enum Keys { Left, Right, Up, Down, /* ... */ };

Aby zapobiegać takim sytuacjom, w Javie i C# stałe wyliczeniowe muszą być kwalifikowane nazwą odpowiedniego typu – używa się więc Sides.Left i Keys.Left. W C++ jest jednak inaczej, gdyż blok enum sam w sobie nie tworzy zasięgu (w przeciwieństwie np. do bloków class).

Można temu częściowo zaradzić w następujący sposób:

  1. struct Sides
  2. {
  3.     enum _Enum { Left = -1, Middle = 0, Right = 1 };
  4. };
  5. typedef Sides::_Enum Side;

dzięki czemu możemy z naszego enuma korzystać tak:

  1. Side foo;
  2. foo = Sides::Left;  // OK
  3. foo = 1; // błąd - nie można przypisać liczby
  4. foo = Right; // błąd - Right nie jest w przestrzeni globalnej

Różnica względem wspomnianych dwóch języków polega na tym, że nazwa typu wyliczeniowego (Side) oraz kwalifikator stałych (Sides) nie są takie same. Wydaje się jednak (przynajmniej mi się tak wydaje :]), że w tym przypadku takie rozróżnienie jest logicznie poprawne i wygląda nawet czytelniej niż gdyby obie nazwy były identyczne.
Trik ten można naturalnie opakować w makro, które umożliwi łatwe tworzenie typów wyliczeniowych z kwalifikowanymi nazwami stałych. Nie poprawi to oczywiście funkcjonalności enumów w C++, ale przynajmniej sprawi, że będą ładniej wyglądały :D

Tags: , ,
Author: Xion, posted under Programming »


10 comments for post “Wyliczenia kwalifikowane”.
  1. aaa:
    July 2nd, 2008 o 3:04

    Hm, może jest późno, ale nie rozumiem chyba dlaczego jest

    foo = Sides::Left;

    a nie

    foo = Side::Left;

  2. RedHot:
    July 2nd, 2008 o 13:30

    Jest wcześnie i też się zastanawiam :s

  3. agent_J:
    July 2nd, 2008 o 14:48

    aaa: Ponieważ Side to typedef na Sides::_Enum, a używamy Sides::Left a nie Sides::_Enum::Left :P

  4. nilphilus:
    July 2nd, 2008 o 20:33

    a ja od razu skumałem :P

  5. yarpen:
    July 7th, 2008 o 11:12

    Dodajmy jeszcze, ze nazwy zaczynajace sie podkresleniem + duza litera sa zarezerwowane dla kompilatora, wiec technicznie Sides::_Enum lamie zgodnosc ze standardem :) Sides::Enum bedzie OK. Wiecej – http://powerof2games.com/node/30

  6. Luq:
    July 12th, 2008 o 3:06

    ale można łatwiej

    1. struct S
    2. {   enum { E0, E1, ENUM_END };
    3. };
  7. Xion:
    July 12th, 2008 o 9:23

    Wtedy rzeczywiście piszemy S::E0, S::E1, itd., ale nie da się w ogóle stworzyć zmiennej należącego do tego typu wyliczeniowego. Bo

    1. S var;

    nią nie będzie.

    yarpen: Zawsze myślałem, że dotyczy to tylko nazw z dwoma podkreśleniami na początku i niekoniecznie z wielką literą potem (przykład: __cplusplus). Możesz podać jakieś źródło tej informacji? (Oczywiście najlepiej odpowiedni punkt standardu :D).

  8. Luq:
    July 13th, 2008 o 11:38

    [up] no racja ale…(hehe zawsze jest jakieś ale:p)

    1. struct S
    2. {   typedef enum { E0, E1, ENUM_END } Enum;
    3. };

    i już możemy sobie zrobić:)

    1. S::Enum deklaracja;
    2. deklaracja = S::E0;//etc. itd.
  9. manfred:
    March 21st, 2009 o 16:27

    USO 14882:2003, 17.4.3.1.2, pierwszy myślnik: Each name that contains a double underscore (_ _) or begins with an underscore followed by an uppercase
    letter (2.11) is reserved to the implementation for any use.

  10. manfred:
    March 21st, 2009 o 16:28

    argh. ISO oczywiście :)

Comments are disabled.
 


© 2019 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.