enum X : int
(C #) lub enum class X : int
(C ++ 11) jest typem, który ma ukryte wewnętrzne pole int
, które może pomieścić dowolną wartość. Ponadto X
w wyliczeniu zdefiniowano szereg predefiniowanych stałych . Możliwe jest rzutowanie wyliczenia na jego liczbę całkowitą i odwrotnie. Dotyczy to zarówno C #, jak i C ++ 11.
W języku C # wyliczenia są używane nie tylko do przechowywania pojedynczych wartości, ale także do przechowywania bitowych kombinacji flag, zgodnie z zaleceniami Microsoftu . Takie wyliczenia są (zwykle, ale niekoniecznie) ozdobione [Flags]
atrybutem. Aby ułatwić życie programistom, operatory bitowe (LUB, AND itd.) Są przeciążone, dzięki czemu można łatwo zrobić coś takiego (C #):
void M(NumericType flags);
M(NumericType.Sign | NumericType.ZeroPadding);
Jestem doświadczonym programistą C #, ale programuję C ++ dopiero od kilku dni i nie znam konwencji C ++. Zamierzam używać wyliczenia C ++ 11 dokładnie w taki sam sposób, jak to robiłem w C #. W C ++ 11 operatory bitowe w wyliczeniach zakresowych nie są przeciążone, więc chciałem je przeciążyć .
To wywołało debatę, a opinie wydają się różnić między trzema opcjami:
Zmienna typu wyliczeniowego służy do przechowywania pola bitowego, podobnie do C #:
void M(NumericType flags); // With operator overloading: M(NumericType::Sign | NumericType::ZeroPadding); // Without operator overloading: M(static_cast<NumericType>(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding)));
Byłoby to jednak sprzeczne z silnie wpisaną filozofią wyliczania enum w C ++ 11.
Użyj zwykłej liczby całkowitej, jeśli chcesz przechowywać bitową kombinację wyliczeń:
void M(int flags); M(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding));
Ale to zredukowałoby wszystko do
int
, pozostawiając cię bez pojęcia, jaki typ powinieneś zastosować w metodzie.Napisz osobną klasę, która przeciąża operatorów i przechowuje flagi bitowe w ukrytym polu liczby całkowitej:
class NumericTypeFlags { unsigned flags_; public: NumericTypeFlags () : flags_(0) {} NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {} //...define BITWISE test/set operations }; void M(NumericTypeFlags flags); M(NumericType::Sign | NumericType::ZeroPadding);
( pełny kod autorstwa użytkownika315052 )
Ale wtedy nie masz IntelliSense ani żadnego wsparcia, które wskazywałoby na możliwe wartości.
Wiem, że to subiektywne pytanie , ale: Jakiego podejścia powinienem użyć? Jakie podejście jest najbardziej rozpowszechnione w C ++? Jakiego podejścia używasz w przypadku pól bitowych i dlaczego ?
Oczywiście, ponieważ wszystkie trzy podejścia działają, szukam przyczyn faktycznych i technicznych, ogólnie przyjętych konwencji, a nie tylko osobistych preferencji.
Na przykład, z powodu mojego tła w języku C #, zwykle używam podejścia 1 w C ++. Ma to tę dodatkową zaletę, że moje środowisko programistyczne może podpowiedzieć mi o możliwych wartościach, a przy przeciążonych operatorach enum jest to łatwe do napisania i zrozumienia oraz całkiem czyste. Podpis metody pokazuje wyraźnie, jakiej wartości oczekuje. Ale większość ludzi tutaj się ze mną nie zgadza, prawdopodobnie z ważnego powodu.
enum E { A = 1, B = 2, C = 4, };
zakresu jest 0..7
(3 bity). W związku z tym standard C ++ wyraźnie gwarantuje, że nr 1 zawsze będzie realną opcją. [W szczególności enum class
domyślnie, enum class : int
chyba że określono inaczej, a zatem zawsze ma ustalony typ bazowy.])