2013-02-20 27 views
33

Czy to możliwe do ustalenia liczność C++ enum class:Czy można określić liczbę elementów klasy C++ enum?

enum class Example { A, B, C, D, E }; 

Próbowałem użyć sizeof, jednak zwraca rozmiar elementu enum.

sizeof(Example); // Returns 4 (on my architecture) 

Czy istnieje standardowy sposób uzyskania liczności (5 w moim przykładzie)?

+0

Pomyślałem, że mógł istnieć specyficzny mechanizm C++ 11. – bquenin

+4

To nie jest duplikat, nawiasem mówiąc. 'enum' i' enum class'es to bardzo różne koncepcje. – Shoe

+0

@ Shoe ... czy naprawdę są? –

Odpowiedz

43

Nie bezpośrednio, ale można użyć następującej sztuczki:

enum class Example { A, B, C, D, E, Count }; 

Następnie liczność jest dostępny jako (int)Example::Count.

Oczywiście działa to tylko ładnie, jeśli pozwalasz na automatyczne przypisywanie wartości wyliczenia, poczynając od 0. Jeśli tak nie jest, możesz ręcznie przypisać odpowiednią liczność do zliczenia, co tak naprawdę nie różni się od konieczności utrzymać oddzielną stałą i tak:

enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 }; 

jedną wadą jest to, że kompilator pozwoli Ci korzystać Example::Count jako argument na wartość enum - więc należy być ostrożnym, jeśli to wykorzystać! (Osobiście uważam, że nie stanowi to problemu w praktyce).

+5

Chociaż mogłoby to zostać zerwane, jeśli którakolwiek z wartości jest ustawiona na wartość inną niż domyślna. Dlatego należy zachować ostrożność. – juanchopanza

+0

OK, dziękuję za podpowiedź – bquenin

+1

Wartości wyliczeniowe są bezpieczne w klasie wyliczeniowej, więc "Liczba" będzie typem Przykład tutaj, a nie int, prawda? Musiałbyś najpierw rzucić "Count" na int, aby użyć go do rozmiaru. –

1

Nie, musisz napisać to w kodzie.

3

Jedna sztuczka, którą możesz spróbować, to dodać wartość wyliczenia na końcu listy i użyć jej jako rozmiaru. W przykładzie

enum class Example { A, B, C, D, E, ExampleCount }; 
5
enum class TEST 
{ 
    BEGIN = __LINE__ 
    , ONE 
    , TWO 
    , NUMBER = __LINE__ - BEGIN - 1 
}; 

auto const TEST_SIZE = TEST::NUMBER; 
+0

Sprytny! Oczywiście nie ma żadnych komentarzy ani nietypowych odstępów, a w przypadku naprawdę dużych plików źródłowych podstawowy typ wartości może być większy niż byłby w innym przypadku. –

+0

@ Kyle Strand: jest ten problem: za pomocą char i masz więcej niż 256 modułów wyliczających. Ale kompilator ma dobre maniery, aby powiadomić cię o przycięciach itp. __LINE__ jest literałem całkowitym i użycie #line ma ograniczenie [1, 2147483647] – UglyCoder

+0

Ah, okay. Mimo to, nawet wyliczenie, które w innym przypadku byłoby "krótkie", mogłoby zostać podbite do "int", np. podczas tworzenia jedności. (Powiedziałbym, że jest to raczej problem z budowaniem jedności niż z proponowaną sztuczką.) –

6
constexpr auto TEST_START_LINE = __LINE__; 
enum class TEST { // Subtract extra lines from TEST_SIZE if an entry takes more than one 
    ONE = 7 
    , TWO = 6 
    , THREE = 9 
}; 
constexpr auto TEST_SIZE = __LINE__ - TEST_START_LINE - 3; 

ta pochodzi od UglyCoder's answer ale poprawia go na trzy sposoby.

  • Brak dodatkowych elementów w enum type_safe (BEGIN i SIZE) (Cameron's answer ma również ten problem.)
    • Kompilator nie będzie narzekać im bycia brakuje instrukcji switch (istotny problem)
    • Nie można ich nieumyślnie przekazać funkcjom oczekującym wyliczenia. (nie jest to typowy problem)
  • Nie wymaga odlewania do użytku. (Cameron's answer ma również ten problem.)
  • Odejmowanie nie zepsuje się rozmiarem typu klasy wyliczeniowej.

Zachowuje on przewagę nad Cameron's answer, że rachmistrzom można przypisać dowolne wartości.

Problem (dzielony z UglyCoder, ale nie z Cameron) polega na tym, że powoduje, że znaki nowej linii i komentarze są znaczące ... co jest nieoczekiwane. Tak więc ktoś mógłby dodać wpis z białymi znakami lub komentarzem bez dostosowania obliczeń TEST_SIZE.

1

Można również rozważyć static_cast<int>(Example::E) + 1, który eliminuje dodatkowy element.

+2

Ta odpowiedź jest poprawna dla tego konkretnego problemu z programowaniem, ale generalnie jest daleka od eleganckiej i podatnej na błędy. Wyliczenie może zostać rozszerzone o nowe wartości w przyszłości, które mogą zastąpić 'Przykład :: E' jako ostatnią wartość w wyliczeniu. Nawet jeśli tak nie jest, wartość literowa "Przykład :: E" może się zmienić. – Matthias

2

Jest jeszcze jedna sztuczka opiera się na X() - makra: obraz, masz następujące wyliczenia:

enum MyEnum {BOX, RECT}; 

sformatować do:

#define MyEnumDef \ 
    X(BOX), \ 
    X(RECT) 

Następnie poniższy kod definiuje typ enum:

enum MyEnum 
{ 
#define X(val) val 
    MyEnumDef 
#undef X 
}; 

i następujący kod oblicza liczbę elementów eNUM:

template <typename ... T> void null(T...) {} 

template <typename ... T> 
constexpr size_t countLength(T ... args) 
{ 
    null(args...); //kill warnings 
    return sizeof...(args); 
} 

constexpr size_t enumLength() 
{ 
#define XValue(val) #val 
    return countLength(MyEnumDef); 
#undef XValue 
} 

... 
std::array<int, enumLength()> some_arr; //enumLength() is compile-time 
std::cout << enumLength() << std::endl; //result is: 2 
...