(Bagaimana) saya bisa menghitung item dalam enum?

98

Pertanyaan ini muncul di benak saya, ketika saya memiliki sesuatu seperti

enum Folders {FA, FB, FC};

dan ingin membuat berbagai wadah untuk setiap folder:

ContainerClass*m_containers[3];
....
m_containers[FA] = ...; // etc.

(Menggunakan peta, ini jauh lebih elegan untuk digunakan std::map<Folders, ContainerClass*> m_containers;:)

Tetapi untuk kembali ke pertanyaan awal saya: Bagaimana jika saya tidak ingin membuat kode keras ukuran array, adakah cara untuk mengetahui berapa banyak item yang ada di Folder? (Tanpa bergantung pada misalnya FCmenjadi item terakhir dalam daftar yang akan memungkinkan sesuatu seperti ContainerClass*m_containers[FC+1]jika saya tidak salah.)

fuenfundachtzig
sumber
Posting ini mungkin menjawab pertanyaan Anda: stackoverflow.com/questions/1390703/enumerate-over-an-enum-in-c .
StackedCrooked
1
Pertanyaannya agak kurang spesifik. Sesuai standar C ++, int(FA) | int(FB) | int (FC)juga merupakan nilai legal untuk Foldersvariabel. Jika Anda mengukur m_containerssehingga Foldersvariabel apa pun adalah indeks yang valid, [FC+1]tidak akan cukup besar.
MSalters
Saya telah menanyakan hal yang sangat terkait di stackoverflow.com/questions/12972317/count-on-enum-c-automatic
sergiol
Saya akan merekomendasikan Anda solusi dari stackoverflow.com/a/60216003/12894563 dan varian yang ditingkatkan dari stackoverflow.com/a/60271408/12894563
ixjxk

Jawaban:

123

Sebenarnya tidak ada cara yang baik untuk melakukan ini, biasanya Anda melihat item tambahan di enum, yaitu

enum foobar {foo, bar, baz, quz, FOOBAR_NR_ITEMS};

Jadi, Anda dapat melakukan:

int fuz[FOOBAR_NR_ITEMS];

Masih tidak terlalu bagus.

Tapi tentu saja Anda menyadari bahwa jumlah item dalam enum tidaklah aman, misalnya

enum foobar {foo, bar = 5, baz, quz = 20};

jumlah item akan menjadi 4, tetapi nilai integer dari nilai enum akan keluar dari rentang indeks array. Menggunakan nilai enum untuk pengindeksan array tidak aman, Anda harus mempertimbangkan opsi lain.

edit: seperti yang diminta, membuat entri khusus lebih menonjol.

yang mana
sumber
27
Sebut saja LAST atau ALWAYS_AT_END atau sesuatu yang tidak terlalu samar. Buat itu menonjol . Sehingga pengelola berikutnya tidak secara tidak sengaja menambahkan entri baru setelah Anda mengakhiri penanda.
Martin York
3
Baik atau buruk, ini adalah pendekatan yang kami ambil dalam organisasi kami. Kami biasanya menyebutnya FINAL_enumname_ENTRY, seperti FINAL_foobar_ENTRY. Saya juga telah melihat orang-orang menggunakan variabel FOOBAR_COUNT const statis terpisah yang ditentukan tepat setelah deklarasi enum, pendekatan yang sedikit lebih rentan terhadap kesalahan.
Darryl
1
Setidaknya cukup mudah untuk melihat bahwa "enum foo {a = 10, LAST}" akan menjadi ganjil. Dan saya pikir "int arr [LAST]" akan menjadi 11 item dalam kasus ini, bukan 2, jadi sebagian besar kode akan berfungsi (tetapi Anda membuang-buang memori pada nilai indeks yang tidak valid)
Code Abominator
32

Untuk C ++, ada berbagai teknik enum tipe aman yang tersedia, dan beberapa di antaranya (seperti Boost.Enum yang diusulkan tetapi tidak pernah dikirimkan ) menyertakan dukungan untuk mendapatkan ukuran enum.

Pendekatan paling sederhana, yang berfungsi di C dan juga C ++, adalah mengadopsi konvensi mendeklarasikan nilai ... MAX untuk setiap jenis enum Anda:

enum Folders { FA, FB, FC, Folders_MAX = FC };
ContainerClass *m_containers[Folders_MAX + 1];
....
m_containers[FA] = ...; // etc.

Sunting : Mengenai { FA, FB, FC, Folders_MAX = FC}versus {FA, FB, FC, Folders_MAX]: Saya lebih suka menyetel nilai ... MAX ke nilai legal terakhir dari enum karena beberapa alasan:

  1. Nama konstanta secara teknis lebih akurat (karena Folders_MAXmemberikan nilai enum semaksimal mungkin).
  2. Secara pribadi, saya merasa Folders_MAX = FCmenonjol dari entri lain sedikit lebih (membuatnya sedikit lebih sulit untuk secara tidak sengaja menambahkan nilai enum tanpa memperbarui nilai maksimal, masalah yang direferensikan Martin York).
  3. GCC menyertakan peringatan bermanfaat seperti "nilai enumerasi tidak disertakan dalam sakelar" untuk kode seperti berikut ini. Membiarkan Folders_MAX == FC + 1 merusak peringatan itu, karena Anda berakhir dengan sekumpulan ... MAX nilai pencacahan yang seharusnya tidak pernah disertakan dalam sakelar.
saklar (folder) 
{
  kasus FA: ...;
  kasus FB: ...;
  // Ups, lupakan FC!
}
Josh Kelley
sumber
3
Mengapa tidak melakukan enum Folders { FA, FB, FC, Folders_MAX }; ContainerClass *m_containers[Folders_MAX];:?
Bill
1
Saya lebih suka menjelaskan bahwa yang terakhir adalah angka, dan mereka semua memiliki nama yang sama berkat:struct SomeEnum { enum type {FA, FB, FC, NB__};};
Luc Hermitte
2
Sebenarnya saya merasa peringatan yang "membantu" itu menyebalkan. Saya suka peringatan yang baik, saya selalu menetapkan -Wall -pedantic, dll. Saat saya mengembangkan, tetapi peringatan ini bodoh. Hanya ada beberapa yang lebih buruk, seperti menyarankan orang tua untuk && || dan & ^ | prioritas operator. Maksud saya, saya pikir java adalah bahasa babysitter, apa yang terjadi pada C dan C ++ ...
yang
2
Kelemahan dari memiliki Folders_max = FC adalah Anda harus mengubahnya setiap kali Anda menambahkan sesuatu ke enum!
Étienne
2
enum Folder {FA, FB, FC, Folders_MIN = FA, Folders_MAX = FC}; Hanya untuk menekankan bahwa ini berguna untuk iterasi?
gjpc
7

Bagaimana dengan ciri-ciri, dalam gaya STL? Misalnya:

enum Foo
{
    Bar,
    Baz
};

menulis sebuah

std::numeric_limits<enum Foo>::max()

spesialisasi (mungkin constexpr jika Anda menggunakan c ++ 11). Kemudian, dalam kode pengujian Anda, berikan pernyataan statis apa pun untuk mempertahankan batasan yang std :: numeric_limits :: max () = last_item.

Wojciech Migda
sumber
2
Sayangnya ini tidak akan berfungsi sesuai jawaban ini .
rr-
3
std::numeric_limits<enum Foo>::max()selalu mengembalikan nol ... (lihat pertanyaan untuk jawaban yang ditautkan) Diuji pada enum normal ( enum Foo { ... }), enum dengan petunjuk tipe ( enum class Foo : uint8_t { ... }) dengan gcc 5.2.0 @ Linux dan MinGW 4.9.3 @ Windows.
rr-
1
(... dan dalam kasus std::numeric_limits<std::underlying_type<Foo>::type>::max(), ini mengembalikan nilai maksimum dari tipe yang mendasarinya yaitu 0xFFFFFFFF untuk bilangan bulat 32-bit, yang tidak berguna dalam konteks ini.)
rr-
1
Aneh, karena saya memiliki kode kerja yang melakukan apa yang saya jelaskan. namespace gx { enum struct DnaNucleobase : char { A, C, G, T }; } Kemudian: namespace std { template<> struct numeric_limits<enum ::gx::DnaNucleobase> { typedef enum ::gx::DnaNucleobase value_type; static constexpr value_type max() { return value_type::T; } (...) Dan std::cout << std::numeric_limits<::gx::DnaNucleobase>::max() << std::endl;mencetak hasil yang diharapkan. Diuji dengan rasa gcc 5.2.1 dan 4.8 / 4.9.
Wojciech Migda
2
-1; ping saya jika Anda mengubah jawabannya, sehingga saya dapat membatalkan suara negatif. Jawaban ini adalah kesepakatan yang buruk untuk diikuti. Ini adalah penyalahgunaan konsep numeric_limits<T>::max(). Satu-satunya hal yang dapat dikembalikan oleh fungsi secara wajar adalah nilai enumerasi tertinggi. Itu akan kembali 2, tetapi OP (dalam kasus khusus ini) akan membutuhkannya untuk kembali 3. Setelah Anda memiliki nilai non-default untuk enum ( FB = 2057), semua taruhan dibatalkan, bahkan tidak + 1dapat meretas kesalahan off-by-one. Jika ada numeric_limits<T>::number_of_elements_of_the_set()(atau nama yang lebih pendek), itu bisa digunakan tanpa ambiguitas.
Merlyn Morgan-Graham
3

Tambahkan entri, di akhir enum Anda, yang disebut Folders_MAX atau sesuatu yang serupa dan gunakan nilai ini saat menginisialisasi array Anda.

ContainerClass* m_containers[Folders_MAX];
Kevin Doyon
sumber
2

Saya suka menggunakan enum sebagai argumen untuk fungsi saya. Ini adalah cara yang mudah untuk memberikan daftar tetap "opsi". Masalah dengan jawaban pilihan teratas di sini adalah bahwa dengan menggunakan itu, klien dapat menentukan "opsi tidak valid". Sebagai spin off, saya sarankan melakukan hal yang pada dasarnya sama, tetapi gunakan int konstan di luar enum untuk menentukan jumlah mereka.

enum foobar { foo, bar, baz, quz };
const int FOOBAR_NR_ITEMS=4;

Ini tidak menyenangkan, tetapi ini adalah solusi yang bersih jika Anda tidak mengubah enum tanpa memperbarui konstanta.

BuvinJ
sumber
1

Saya benar-benar tidak melihat cara apa pun untuk benar-benar mendapatkan jumlah nilai dalam pencacahan di C ++. Salah satu solusi yang disebutkan sebelumnya berfungsi selama Anda tidak menentukan nilai enumerasi Anda jika Anda menentukan nilai yang mungkin Anda hadapi dalam situasi di mana Anda membuat array terlalu besar atau terlalu kecil

enum example{ test1 = -2, test2 = -1, test3 = 0, test4 = 1, test5 = 2 }

dalam contoh tentang ini, hasilnya akan membuat larik 3 item saat Anda membutuhkan larik 5 item

enum example2{ test1 , test2 , test3 , test4 , test5 = 301 }

dalam contoh tentang ini, hasilnya akan membuat array 301 item ketika Anda membutuhkan array yang terdiri dari 5 item

Cara terbaik untuk menyelesaikan masalah ini dalam kasus umum adalah dengan mengulang melalui pencacahan Anda tetapi itu belum sesuai standar sejauh yang saya tahu


sumber