Mengobati enum
s sebagai flag berfungsi dengan baik di C # melalui [Flags]
atribut, tetapi apa cara terbaik untuk melakukan ini di C ++?
Sebagai contoh, saya ingin menulis:
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
seahawk.flags = CanFly | EatsFish | Endangered;
Namun, saya mendapatkan kesalahan kompiler tentang int
/enum
konversi. Apakah ada cara yang lebih baik untuk mengekspresikan hal ini daripada sekadar pengecoran tumpul? Lebih disukai, saya tidak ingin bergantung pada konstruksi dari perpustakaan pihak ke-3 seperti boost atau Qt.
EDIT: Seperti yang ditunjukkan dalam jawaban, saya dapat menghindari kesalahan kompiler dengan menyatakan seahawk.flags
sebagai int
. Namun, saya ingin memiliki mekanisme untuk menegakkan keamanan tipe, jadi seseorang tidak dapat menulis seahawk.flags = HasMaximizeButton
.
[Flags]
atribut berfungsi dengan baik yaitu:[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};
Jawaban:
Cara "benar" adalah mendefinisikan operator bit untuk enum, sebagai:
Dll sisa operator bit. Ubah sesuai kebutuhan jika rentang enum melebihi kisaran int.
sumber
AnimalFlags
yang diwakili oleh ungkapanHasClaws | CanFly
? Ini bukan untuk apaenum
. Gunakan bilangan bulat dan konstanta.(HasClaws | CanFly)
".HasClaws
(= 1) danCanFly
(= 2). Jika sebaliknya Anda hanya menetapkan nilai 1 hingga 4 langsung dan Anda mendapatkan 3, itu mungkin tunggalEatsFish
, atau kombinasi lagi dariHasClaws
danCanFly
. Jika enumerasi Anda menunjukkan status eksklusif hanya maka nilai berturut-turut baik-baik saja, tetapi kombinasi flag membutuhkan nilai tersebut menjadi bit-eksklusif.Catatan (juga topik yang agak aneh): Cara lain untuk membuat bendera unik dapat dilakukan menggunakan sedikit pergeseran. Saya sendiri lebih mudah membaca ini.
Ini dapat menyimpan nilai hingga int sehingga, sebagian besar waktu, 32 bendera yang jelas tercermin dalam jumlah shift.
sumber
Untuk orang-orang malas seperti saya, inilah solusi tempelated untuk menyalin & menempel:
sumber
using
jika perlu, sama sepertirel_ops
.Catatan jika Anda bekerja di lingkungan Windows, ada
DEFINE_ENUM_FLAG_OPERATORS
makro yang didefinisikan di winnt.h yang melakukan pekerjaan untuk Anda. Jadi dalam hal ini, Anda dapat melakukan ini:sumber
Apa jenis variabel seahawk.flags?
Dalam C ++ standar, enumerasi tidak aman untuk tipe. Mereka adalah bilangan bulat yang efektif.
AnimalFlags TIDAK boleh menjadi tipe variabel Anda. Variabel Anda harus int dan kesalahannya akan hilang.
Menempatkan nilai heksadesimal seperti yang disarankan orang lain tidak diperlukan. Tidak ada bedanya.
Nilai enum adalah tipe int secara default. Jadi, Anda pasti bisa bitwise ATAU menggabungkannya dan menggabungkannya dan menyimpan hasilnya dalam sebuah int.
Tipe enum adalah subset terbatas int yang nilainya merupakan salah satu dari nilai yang disebutkan. Karenanya, saat Anda membuat beberapa nilai baru di luar rentang itu, Anda tidak bisa menetapkannya tanpa melakukan casting ke variabel tipe enum Anda.
Anda juga dapat mengubah tipe nilai enum jika diinginkan, tetapi tidak ada gunanya untuk pertanyaan ini.
EDIT: Poster mengatakan mereka peduli dengan keamanan jenis dan mereka tidak menginginkan nilai yang seharusnya tidak ada di dalam tipe int.
Tapi itu akan menjadi tipe yang tidak aman untuk menempatkan nilai di luar rentang AnimalFlags di dalam variabel tipe AnimalFlags.
Ada cara aman untuk memeriksa di luar nilai rentang meskipun di dalam tipe int ...
Di atas tidak menghentikan Anda dari menempatkan bendera tidak valid dari enum berbeda yang memiliki nilai 1,2,4, atau 8.
Jika Anda menginginkan keamanan tipe absolut, Anda cukup membuat std :: set dan menyimpan setiap flag di dalamnya. Ini bukan ruang efisien, tetapi tipe aman dan memberi Anda kemampuan yang sama seperti int bitflag.
C ++ 0x note: Enums yang diketik dengan kuat
Dalam C ++ 0x akhirnya Anda bisa mengetikkan nilai safe enum ....
sumber
HasClaws | CanFly
adalah beberapa tipe integer, tetapi tipenyaHasClaws
adalahAnimalFlags
, bukan tipe integer.max(|emin| − K, |emax|)
dan sama dengan(1u<<M) - 1
, di manaM
bilangan bulat non-negatif. "int
.enum
tidak secara teknis defaultint
sebagai tipe yang mendasarinya (baik pre-C ++ 11 (IIRC), atau post-C ++ 11 ketika tidak ada tipe dasar yang ditentukan), meskipunenum class
demikian . Sebaliknya, tipe yang mendasarinya default ke sesuatu yang cukup besar untuk mewakili semua enumerator, dengan satu-satunya aturan keras yang nyata bahwa itu hanya lebih besar daripadaint
jika secara eksplisit perlu . Pada dasarnya, tipe yang mendasarinya ditentukan sebagai (diparafrasekan) "apa pun yang bekerja, tapi itu mungkinint
kecuali enumerator terlalu besar untukint
".Saya menemukan jawaban yang saat ini diterima oleh eidolon terlalu berbahaya. Pengoptimal kompiler mungkin membuat asumsi tentang nilai yang mungkin di enum dan Anda mungkin mendapatkan sampah kembali dengan nilai yang tidak valid. Dan biasanya tidak ada yang ingin mendefinisikan semua permutasi yang mungkin dalam flag enum.
Seperti yang dinyatakan oleh Brian R. Bondy di bawah ini, jika Anda menggunakan C ++ 11 (yang semua orang seharusnya, itu bagus) Anda sekarang dapat melakukan ini dengan lebih mudah dengan
enum class
:Ini memastikan ukuran dan kisaran nilai yang stabil dengan menentukan jenis enum, menghambat downcasting enum otomatis ke ints dll. Dengan menggunakan
enum class
, dan menggunakanconstexpr
untuk memastikan kode untuk operator mendapatkan inline dan dengan demikian secepat nomor biasa.Untuk orang-orang yang terjebak dengan dialek pra-11 C ++
Jika saya terjebak dengan kompiler yang tidak mendukung C ++ 11, saya akan menggunakan pembungkus tipe int di kelas yang kemudian hanya mengizinkan penggunaan operator bitwise dan tipe dari enum itu untuk menetapkan nilainya:
Anda dapat mendefinisikan ini cukup seperti enum + typedef biasa:
Dan penggunaannya juga mirip:
Dan Anda juga bisa mengesampingkan tipe dasar untuk enum biner-stabil (seperti C ++ 11's
enum foo : type
) menggunakan parameter templat kedua, yaitutypedef SafeEnum<enum TFlags_,uint8_t> TFlags;
.Saya menandai
operator bool
override denganexplicit
kata kunci C ++ 11 untuk mencegahnya menghasilkan konversi int, karena hal itu dapat menyebabkan set flag pada akhirnya diciutkan menjadi 0 atau 1 saat menuliskannya. Jika Anda tidak dapat menggunakan C ++ 11, biarkan kelebihan itu dan tulis ulang syarat pertama dalam contoh penggunaan sebagai(myFlags & EFlagTwo) == EFlagTwo
.sumber
std::underlying_type
alih-alih mengkode jenis tertentu, atau bahwa tipe yang mendasarinya disediakan dan digunakan sebagai jenis alias alih-alih secara langsung. Dengan begitu, perubahan pada tipe yang mendasarinya akan menyebar secara otomatis, alih-alih harus dilakukan secara manual.Cara termudah untuk melakukan ini seperti yang ditunjukkan di sini , menggunakan bitet kelas perpustakaan standar .
Untuk meniru fitur C # dengan cara yang aman, Anda harus menulis pembungkus template di sekitar bitset, mengganti argumen int dengan enum yang diberikan sebagai parameter tipe ke templat. Sesuatu seperti:
sumber
Menurut pendapat saya sejauh ini tidak ada jawaban yang ideal. Menjadi ideal saya harapkan solusinya:
==
,!=
,=
,&
,&=
,|
,|=
dan~
operator dalam arti konvensional (yaitua & b
)if (a & b)...
Sebagian besar solusi sejauh ini jatuh pada poin 2 atau 3. WebDancer adalah penutup dalam pendapat saya tetapi gagal pada poin 3 dan perlu diulang untuk setiap enum.
Solusi yang saya usulkan adalah versi umum dari WebDancer yang juga membahas poin 3:
Ini menciptakan kelebihan dari operator yang diperlukan tetapi menggunakan SFINAE untuk membatasi mereka untuk jenis yang disebutkan. Perhatikan bahwa untuk kepentingan singkatnya saya belum mendefinisikan semua operator tetapi satu-satunya yang berbeda adalah
&
. Operator saat ini bersifat global (yaitu berlaku untuk semua jenis yang disebutkan) tetapi ini dapat dikurangi baik dengan menempatkan kelebihan beban dalam ruang nama (apa yang saya lakukan), atau dengan menambahkan kondisi SFINAE tambahan (mungkin menggunakan tipe dasar tertentu, atau alias tipe khusus yang dibuat khusus ). Iniunderlying_type_t
adalah fitur C ++ 14 tetapi tampaknya didukung dengan baik dan mudah ditiru untuk C ++ 11 dengan sederhanatemplate<typename T> using underlying_type_t = underlying_type<T>::type;
sumber
Standar C ++ secara eksplisit membicarakan hal ini, lihat bagian "17.5.2.1.3 Jenis Bitmask":
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf
Dengan "templat" ini, Anda dapat:
Dan serupa untuk operator lainnya. Perhatikan juga "constexpr", diperlukan jika Anda ingin kompiler dapat menjalankan waktu kompilasi operator.
Jika Anda menggunakan C ++ / CLI dan ingin dapat menetapkan untuk enum anggota kelas ref Anda perlu menggunakan referensi pelacakan sebagai gantinya:
CATATAN: Sampel ini tidak lengkap, lihat bagian "17.5.2.1.3 Jenis bitmask" untuk satu set lengkap operator.
sumber
Saya menemukan diri saya mengajukan pertanyaan yang sama dan menghasilkan solusi berbasis C ++ 11 yang umum, mirip dengan soru:
Tampilannya bisa ditingkatkan sesuai selera. Maka dapat digunakan seperti:
sumber
Jika kompiler Anda belum mendukung enums yang sangat diketik, Anda dapat melihat artikel berikut dari sumber c ++:
Dari abstrak:
sumber
Saya menggunakan makro berikut:
Ini mirip dengan yang disebutkan di atas tetapi memiliki beberapa perbaikan:
int
)Itu perlu menyertakan type_traits:
sumber
Saya ingin menguraikan jawaban Uliwitness , memperbaiki kodenya untuk C ++ 98 dan menggunakan idiom Safe Bool , karena kurangnya
std::underlying_type<>
template danexplicit
kata kunci dalam versi C ++ di bawah C ++ 11.Saya juga memodifikasinya sehingga nilai enum dapat berurutan tanpa penugasan eksplisit, sehingga Anda dapat memilikinya
Anda kemudian bisa mendapatkan nilai bendera mentah dengan
Ini kodenya.
sumber
Berikut adalah opsi untuk bitmasks jika Anda tidak benar-benar menggunakan nilai enum individu (mis. Anda tidak perlu mematikannya) ... dan jika Anda tidak khawatir tentang mempertahankan kompatibilitas biner yaitu: Anda tidak peduli di mana bit Anda tinggal ... yang mungkin Anda berada. Anda juga sebaiknya tidak terlalu peduli dengan pelingkupan dan kontrol akses. Hmmm, enum memiliki beberapa properti bagus untuk bidang-bit ... ingin tahu apakah ada yang pernah mencobanya :)
Kita dapat melihat bahwa hidup ini hebat, kita memiliki nilai-nilai tersendiri, dan kita memiliki int bagus untuk & dan | untuk isi hati kita, yang masih memiliki konteks apa artinya bit. Semuanya konsisten dan dapat diprediksi ... bagi saya ... selama saya tetap menggunakan kompiler VC ++ Microsoft dengan Pembaruan 3 pada Win10 x64 dan jangan menyentuh bendera kompiler saya :)
Meskipun semuanya hebat ... kami memiliki beberapa konteks mengenai arti bendera sekarang, karena ini bersatu dengan bitfield di dunia nyata yang mengerikan di mana program Anda mungkin bertanggung jawab atas lebih dari satu tugas terpisah yang dapat Anda lakukan masih secara tidak sengaja (cukup mudah) menghancurkan dua bidang bendera dari serikat yang berbeda bersama-sama (katakanlah, AnimalProperties dan ObjectProperties, karena keduanya int), mencampur semua bit milik Anda, yang merupakan bug mengerikan untuk dilacak ... dan bagaimana saya tahu banyak orang di pos ini tidak terlalu sering menggunakan bitmask, karena membangunnya mudah dan mempertahankannya sulit.
Jadi, Anda menjadikan deklarasi serikat pribadi untuk mencegah akses langsung ke "Bendera", dan harus menambahkan getter / setter dan kelebihan operator, lalu buat makro untuk semua itu, dan Anda pada dasarnya segera kembali ke tempat Anda memulai ketika Anda mencoba lakukan ini dengan Enum.
Sayangnya jika Anda ingin kode Anda portabel, saya tidak berpikir ada cara untuk A) menjamin tata letak bit atau B) menentukan tata letak bit pada waktu kompilasi (sehingga Anda dapat melacaknya dan setidaknya mengoreksi perubahan di seluruh versi / platform dll) Diimbangi dalam struct dengan bidang bit
Saat runtime Anda dapat memainkan trik dengan mengatur bidang dan XOR bendera untuk melihat bit mana yang berubah, terdengar sangat jelek bagi saya meskipun ayat memiliki 100% konsisten, platform independen, dan solusi sepenuhnya deterministik yaitu: ENUM.
TL; DR: Jangan dengarkan para pembenci. C ++ bukan bahasa Inggris. Hanya karena definisi literal dari kata kunci yang disingkat yang diwarisi dari C mungkin tidak sesuai dengan penggunaan Anda, tidak berarti Anda tidak boleh menggunakannya ketika definisi C dan C ++ dari kata kunci tersebut benar-benar mencakup case use Anda. Anda juga dapat menggunakan struct untuk memodelkan hal-hal selain struktur, dan kelas untuk hal-hal selain sekolah dan kasta sosial. Anda bisa menggunakan float untuk nilai yang di-ground. Anda dapat menggunakan karakter untuk variabel yang tidak hangus atau orang dalam novel, drama, atau film. Setiap programmer yang masuk ke kamus untuk menentukan arti kata kunci sebelum spesifikasi bahasa adalah ... yah saya akan menahan lidah saya di sana.
Jika Anda ingin kode Anda dimodelkan setelah bahasa yang diucapkan Anda sebaiknya menulis di Objective-C, yang notabene juga menggunakan enum banyak untuk bitfield.
sumber
Hanya gula sintaksis. Tidak ada metadata tambahan.
Operator bendera pada tipe integral hanya berfungsi.
sumber
Saat ini tidak ada dukungan bahasa untuk flag enum, kelas Meta mungkin menambahkan fitur ini secara inheren jika itu akan menjadi bagian dari standar c ++.
Solusi saya adalah membuat fungsi template instantiated enum-only menambahkan dukungan untuk operasi bitwise tipe-aman untuk kelas enum menggunakan tipe yang mendasarinya:
File: EnumClassBitwise.h
Untuk kenyamanan dan untuk mengurangi kesalahan, Anda mungkin ingin membungkus operasi flag bit Anda untuk enum dan untuk integer juga:
File: BitFlags.h
Kemungkinan penggunaan:
sumber
@Xaqq telah memberikan cara aman yang sangat bagus untuk menggunakan flag enum di sini oleh a
flag_set
kelas.Saya menerbitkan kode di GitHub , penggunaannya adalah sebagai berikut:
sumber
Anda membingungkan objek dan koleksi objek. Secara khusus, Anda mengacaukan flag biner dengan set flag biner. Solusi yang tepat akan terlihat seperti ini:
sumber
Inilah solusi saya tanpa perlu banyak kelebihan atau casting:
Saya pikir tidak apa-apa, karena kami mengidentifikasi enum dan int (tidak diketik dengan kuat).
Sama seperti catatan (lebih lama), jika Anda
Saya akan datang dengan ini:
menggunakan daftar inisialisasi C ++ 11 dan
enum class
.sumber
using Flags = unsigned long
di dalam namespace atau struct yang berisi nilai-nilai bendera sendiri sebagai/*static*/ const Flags XY = 0x01
dan seterusnya.Seperti di atas (Kai) atau lakukan hal berikut. Benar-benar enum adalah "Enumerasi", yang ingin Anda lakukan adalah memiliki set, oleh karena itu Anda harus benar-benar menggunakan stl :: set
sumber
Mungkin seperti NS_OPTIONS dari Objective-C.
sumber