Sebuah enum X : int
(C #) atau enum class X : int
(C ++ 11) adalah jenis yang memiliki medan batin tersembunyi int
yang dapat menahan nilai apapun. Selain itu, sejumlah konstanta yang X
telah ditentukan didefinisikan pada enum. Dimungkinkan untuk melemparkan enum ke nilai integernya dan sebaliknya. Ini semua benar dalam C # dan C ++ 11.
Dalam C # enums tidak hanya digunakan untuk menyimpan nilai-nilai individual, tetapi juga untuk menahan kombinasi flag bitwise, sesuai rekomendasi Microsoft . Enum semacam itu (biasanya, tetapi tidak harus) didekorasi dengan [Flags]
atribut. Untuk membuat kehidupan pengembang lebih mudah, operator bitwise (OR, AND, dll ...) kelebihan beban sehingga Anda dapat dengan mudah melakukan sesuatu seperti ini (C #):
void M(NumericType flags);
M(NumericType.Sign | NumericType.ZeroPadding);
Saya seorang pengembang C # yang berpengalaman, tetapi telah memprogram C ++ hanya untuk beberapa hari sekarang, dan saya tidak dikenal dengan konvensi C ++. Saya bermaksud menggunakan enum C ++ 11 dengan cara yang sama persis seperti yang biasa saya lakukan di C #. Dalam C ++ 11 operator bitwise pada scoped enums tidak kelebihan beban, jadi saya ingin membebani mereka .
Ini mengundang debat, dan pendapat tampaknya bervariasi di antara tiga opsi:
Variabel tipe enum digunakan untuk menahan bidang bit, mirip dengan 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)));
Tapi ini akan melawan filosofi enum yang sangat diketik dari enum C ++ 11.
Gunakan bilangan bulat polos jika Anda ingin menyimpan kombinasi bit enum:
void M(int flags); M(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding));
Tapi ini akan mengurangi segalanya menjadi
int
, meninggalkan Anda tanpa petunjuk tentang jenis apa yang harus Anda masukkan ke dalam metode.Tulis kelas terpisah yang akan membebani operator dan menahan bendera bitwise di bidang bilangan bulat tersembunyi:
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);
( kode lengkap oleh pengguna315052 )
Tetapi kemudian Anda tidak memiliki IntelliSense atau dukungan apa pun untuk mengisyaratkan Anda pada nilai-nilai yang mungkin.
Saya tahu ini adalah pertanyaan subyektif , tetapi: Pendekatan apa yang harus saya gunakan? Pendekatan apa, jika ada, yang paling dikenal di C ++? Pendekatan apa yang Anda gunakan ketika berhadapan dengan bidang bit dan mengapa ?
Tentu saja karena ketiga pendekatan ini berhasil, saya mencari alasan faktual dan teknis, konvensi yang diterima secara umum, dan bukan hanya preferensi pribadi.
Sebagai contoh, karena latar belakang C # saya cenderung pergi dengan pendekatan 1 di C ++. Ini memiliki manfaat tambahan bahwa lingkungan pengembangan saya dapat memberi saya petunjuk tentang nilai-nilai yang mungkin, dan dengan operator enum yang berlebihan ini mudah untuk ditulis dan dipahami, dan cukup bersih. Dan metode tanda tangan menunjukkan dengan jelas nilai seperti apa yang diharapkannya. Tetapi kebanyakan orang di sini tidak setuju dengan saya, mungkin karena alasan yang baik.
sumber
enum E { A = 1, B = 2, C = 4, };
, kisarannya adalah0..7
(3 bit). Dengan demikian, standar C ++ secara eksplisit menjamin bahwa # 1 akan selalu menjadi opsi yang layak. [Secara khusus,enum class
default keenum class : int
kecuali ditentukan lain, dan dengan demikian selalu memiliki tipe yang mendasari tetap.])Jawaban:
Cara paling sederhana adalah dengan memberikan operator kelebihan beban sendiri. Saya berpikir untuk membuat makro untuk memperluas kelebihan beban dasar per jenis.
(Perhatikan bahwa itu
type_traits
adalah header C ++ 11 danstd::underlying_type_t
merupakan fitur C ++ 14.)sumber
static_cast<T>
input, tetapi cor gaya C untuk hasil di sini?SBJFrameDrag
didefinisikan dalam kelas dan|
-operator kemudian digunakan dalam definisi kelas yang sama, bagaimana Anda mendefinisikan operator sehingga dapat digunakan di dalam kelas?Secara historis, saya akan selalu menggunakan enumerasi lama (yang diketik dengan lemah) untuk memberi nama konstanta bit, dan hanya menggunakan kelas penyimpanan secara eksplisit untuk menyimpan flag yang dihasilkan. Di sini, tanggung jawab akan berada pada saya untuk memastikan enumerasi saya sesuai dengan jenis penyimpanan, dan untuk melacak hubungan antara bidang dan konstanta terkait.
Saya menyukai gagasan enum yang sangat diketik, tetapi saya tidak terlalu nyaman dengan gagasan bahwa variabel jenis yang dihitung mungkin mengandung nilai yang tidak termasuk dalam konstanta enumerasi itu.
Misalnya, dengan asumsi bitwise atau telah kelebihan beban:
Untuk opsi ke-3 Anda, Anda memerlukan beberapa boilerplate untuk mengekstraksi tipe penyimpanan enumerasi. Dengan asumsi kami ingin memaksakan tipe dasar yang tidak ditandatangani (kami juga dapat menangani yang ditandatangani, dengan sedikit kode lagi):
Ini masih tidak memberi Anda IntelliSense atau pelengkapan otomatis, tetapi deteksi tipe penyimpanan kurang jelek dari yang saya harapkan.
Sekarang, saya memang menemukan alternatif: Anda dapat menentukan tipe penyimpanan untuk enumerasi yang diketik dengan lemah. Bahkan memiliki sintaks yang sama seperti di C #
Karena ini diketik dengan lemah, dan secara implisit mengkonversi ke / dari int (atau jenis penyimpanan apa pun yang Anda pilih), rasanya kurang aneh untuk memiliki nilai yang tidak cocok dengan konstanta yang disebutkan.
The downside adalah bahwa ini digambarkan sebagai "transisi" ...
NB. varian ini menambahkan konstanta enumerasi pada ruang lingkup bersarang dan terlampir, tetapi Anda bisa mengatasinya dengan namespace:
sumber
Anda dapat mendefinisikan flag enum tipe-aman di C ++ 11 dengan menggunakan
std::enable_if
. Ini adalah implementasi yang belum sempurna yang mungkin kehilangan beberapa hal:Catatannumber_of_bits
sayangnya tidak dapat diisi oleh kompiler, karena C ++ tidak memiliki cara untuk melakukan introspeksi nilai-nilai yang mungkin dari sebuah enumerasi.Sunting: Sebenarnya saya berdiri dikoreksi, adalah mungkin untuk mendapatkan isi kompiler
number_of_bits
untuk Anda.Catatan ini dapat menangani (sangat tidak efisien) rentang nilai enum yang tidak berkelanjutan. Katakan saja itu bukan ide yang baik untuk menggunakan hal di atas dengan enum seperti ini atau kegilaan akan terjadi:
Tetapi semua hal yang dianggap sebagai solusi pada akhirnya cukup bermanfaat. Tidak memerlukan bitfiddling sisi pengguna, tipe-aman dan dalam batas-batasnya, seefisien yang didapat (Saya sangat bergantung pada
std::bitset
kualitas implementasi di sini;)
).sumber
saya
bencimembenci makro di C ++ 14 saya sebanyak orang berikutnya, tetapi saya telah menggunakan ini di mana-mana, dan juga secara liberal:Membuat penggunaan sesederhana
Dan, seperti yang mereka katakan, buktinya ada di puding:
Jangan ragu untuk mendefinisikan salah satu dari operator individual yang Anda inginkan, tetapi menurut pendapat saya yang sangat bias, C / C ++ adalah untuk berinteraksi dengan konsep dan aliran tingkat rendah, dan Anda dapat mencabut operator bitwise ini dari tangan saya yang dingin dan mati. dan aku akan bertarung denganmu dengan semua makro yang tidak suci dan mantra yang bisa kubalik untuk menjaga mereka.
sumber
std::enable_if
denganstd::is_enum
untuk membatasi kelebihan operator Anda hanya untuk bekerja dengan tipe yang disebutkan. Saya juga menambahkan operator perbandingan (menggunakanstd::underlying_type
) dan operator bukan logis untuk lebih menjembatani kesenjangan tanpa kehilangan pengetikan yang kuat. Satu-satunya hal yang saya tidak dapat mencocokkan adalah konversi implisit ke bool, tetapiflags != 0
dan!flags
cukup bagi saya.Biasanya Anda akan mendefinisikan satu set nilai integer yang sesuai dengan angka-angka biner bit-set tunggal, kemudian menambahkannya bersama-sama. Ini adalah cara programmer C biasanya melakukannya.
Jadi Anda harus (menggunakan operator bitshift untuk mengatur nilai, misalnya 1 << 2 sama dengan biner 100)
dll
Di C ++ Anda memiliki lebih banyak opsi, tentukan jenis baru dan bukan int (gunakan typedef ) dan tetapkan nilai seperti di atas; atau mendefinisikan bitfield atau vektor bools . 2 yang terakhir sangat efisien ruang dan jauh lebih masuk akal untuk berurusan dengan bendera. Bitfield memiliki keuntungan memberi Anda tipe pengecekan (dan karenanya, intellisense).
Saya akan mengatakan (jelas subyektif) bahwa seorang programmer C ++ harus menggunakan bitfield untuk masalah Anda, tetapi saya cenderung melihat pendekatan #define yang sering digunakan oleh program C dalam program C ++.
Saya kira bitfield adalah yang paling dekat dengan enum C #, mengapa C # mencoba untuk membebani enum menjadi tipe bitfield itu aneh - enum harus benar-benar menjadi tipe "pilihan tunggal".
sumber
0b0100
) sehingga1 << n
formatnya sudah usang.Contoh singkat dari enum-flag di bawah ini, terlihat sangat mirip dengan C #.
Tentang pendekatan, menurut pendapat saya: lebih sedikit kode, lebih sedikit bug, kode lebih baik.
ENUM_FLAGS (T) adalah makro, didefinisikan dalam enum_flags.h (kurang dari 100 baris, gratis untuk digunakan tanpa batasan).
sumber
::type
sana. Diperbaiki: paste.ubuntu.com/23884820Masih ada cara lain untuk menguliti kucing:
Alih-alih membebani operator bit, setidaknya beberapa mungkin lebih suka untuk hanya menambahkan 4 liner untuk membantu Anda menghindari pembatasan yang buruk dari enum yang dicakup:
Memang, Anda harus mengetikkan
ut_cast()
hal itu setiap kali, tetapi di sisi atas, ini menghasilkan kode yang lebih mudah dibaca, dalam arti yang sama seperti menggunakanstatic_cast<>()
, dibandingkan dengan konversi jenis implisit atauoperator uint16_t()
semacamnya.Dan mari kita jujur di sini, menggunakan tipe
Foo
seperti pada kode di atas memiliki bahayanya:Di tempat lain seseorang mungkin melakukan case switch lebih dari variabel
foo
dan tidak berharap itu memegang lebih dari satu nilai ...Jadi mengotori kode dengan
ut_cast()
membantu mengingatkan pembaca bahwa ada sesuatu yang mencurigakan sedang terjadi.sumber