Saya mencoba melakukan sesuatu seperti berikut:
enum E;
void Foo(E e);
enum E {A, B, C};
yang ditolak kompilator. Saya sudah melihat sekilas di Google dan konsensusnya adalah "Anda tidak bisa melakukannya", tetapi saya tidak mengerti mengapa. Adakah yang bisa menjelaskan?
Klarifikasi 2: Saya melakukan ini karena saya memiliki metode pribadi di kelas yang mengambil kata enum, dan saya tidak ingin nilai-nilai enum terbuka - jadi, misalnya, saya tidak ingin ada yang tahu bahwa E didefinisikan sebagai
enum E {
FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}
sebagai proyek X bukanlah sesuatu yang saya ingin pengguna saya ketahui.
Jadi, saya ingin meneruskan mendeklarasikan enum sehingga saya bisa meletakkan metode pribadi di file header, mendeklarasikan enum secara internal di cpp, dan mendistribusikan file library yang dibangun dan header ke orang.
Adapun kompiler - itu GCC.
Jawaban:
Alasan enum tidak dapat diteruskan dinyatakan adalah bahwa tanpa mengetahui nilai-nilai, kompiler tidak dapat mengetahui penyimpanan yang diperlukan untuk variabel enum. C ++ Compiler diperbolehkan untuk menentukan ruang penyimpanan aktual berdasarkan ukuran yang diperlukan untuk memuat semua nilai yang ditentukan. Jika semua yang terlihat adalah deklarasi maju, unit terjemahan tidak dapat mengetahui ukuran penyimpanan apa yang akan dipilih - bisa berupa char atau int, atau yang lainnya.
Dari Bagian 7.2.5 Standar ISO C ++:
Karena penelepon ke fungsi harus mengetahui ukuran parameter untuk mengatur tumpukan panggilan dengan benar, jumlah enumerasi dalam daftar enumerasi harus diketahui sebelum prototipe fungsi.
Pembaruan: Dalam C ++ 0X sintaks untuk kata kunci yang menyatakan jenis enum telah diusulkan dan diterima. Anda dapat melihat proposal di http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf
sumber
struct S; void foo(S s);
(catatan yangfoo
hanya dideklarasikan, tidak didefinisikan), maka tidak ada alasan mengapa kita tidak dapat melakukannyaenum E; void foo(E e);
juga. Dalam kedua kasus, ukurannya tidak diperlukan.Deklarasi maju enum dimungkinkan karena C ++ 11. Sebelumnya, alasan tipe enum tidak dapat diteruskan adalah karena ukuran enumerasi tergantung pada isinya. Selama ukuran enumerasi ditentukan oleh aplikasi, itu dapat dinyatakan maju:
sumber
Saya menambahkan jawaban terkini di sini, mengingat perkembangan terakhir.
Anda dapat meneruskan-mendeklarasikan enum di C ++ 11, selama Anda mendeklarasikan tipe penyimpanannya pada saat yang sama. Sintaksnya terlihat seperti ini:
Bahkan, jika fungsi tidak pernah merujuk pada nilai-nilai enumerasi, Anda tidak perlu deklarasi lengkap sama sekali pada saat itu.
Ini didukung oleh G ++ 4.6 dan seterusnya (
-std=c++0x
atau-std=c++11
dalam versi yang lebih baru). Visual C ++ 2013 mendukung ini; dalam versi sebelumnya ia memiliki semacam dukungan non-standar yang belum saya temukan - saya menemukan beberapa saran bahwa deklarasi maju sederhana itu legal, tetapi YMMV.sumber
enum class
sebagai ekstensi C ++ (sebelum C ++ 11 berbedaenum class
), setidaknya jika saya ingat dengan benar. Kompiler memungkinkan Anda untuk menentukan jenis enum yang mendasarinya, tetapi tidak mendukungenum class
atau meneruskan enum, dan memperingatkan Anda bahwa kualifikasi enumerator dengan cakupan enum adalah ekstensi yang tidak standar. Saya ingat itu bekerja kira-kira sama dengan menentukan tipe yang mendasari di C ++ 11 tidak, kecuali lebih menjengkelkan karena Anda harus menekan peringatan.Maju menyatakan hal-hal dalam C ++ sangat berguna karena secara dramatis mempercepat waktu kompilasi . Anda maju dapat mendeklarasikan beberapa hal di C ++ termasuk:
struct
,class
,function
, dll ...Tapi bisakah Anda meneruskan menyatakan
enum
dalam C ++?Tidak, kamu tidak bisa.
Tapi mengapa tidak membiarkannya? Jika diizinkan, Anda dapat menentukan
enum
jenis Anda di file header Anda, danenum
nilai - nilai Anda di file sumber Anda. Kedengarannya seperti itu harus diizinkan kan?Salah.
Di C ++ tidak ada tipe default untuk
enum
seperti ada di C # (int). Dalam C ++enum
tipe Anda akan ditentukan oleh kompiler untuk menjadi tipe apa pun yang akan cocok dengan rentang nilai yang Anda miliki untuk Andaenum
.Apa artinya?
Ini berarti bahwa
enum
tipe dasar Anda tidak dapat sepenuhnya ditentukan sampai Anda memiliki semua nilai yangenum
ditentukan. Orang mana yang tidak dapat Anda pisahkan dari deklarasi dan definisi Andaenum
. Dan karena itu Anda tidak dapat meneruskan mendeklarasikanenum
dalam C ++.Standar ISO C ++ S7.2.5:
Anda dapat menentukan ukuran tipe yang disebutkan dalam C ++ dengan menggunakan
sizeof
operator. Ukuran dari tipe yang disebutkan adalah ukuran dari tipe yang mendasarinya. Dengan cara ini Anda bisa menebak jenis yang digunakan kompiler untuk Andaenum
.Bagaimana jika Anda menentukan jenis Anda
enum
secara eksplisit seperti ini:Bisakah Anda meneruskan menyatakan Anda
enum
?Tapi mengapa tidak?
Menentukan jenis suatu
enum
sebenarnya bukan bagian dari standar C ++ saat ini. Ini adalah ekstensi VC ++. Ini akan menjadi bagian dari C ++ 0x.Sumber
sumber
[Jawaban saya salah, tetapi saya meninggalkannya di sini karena komentarnya bermanfaat].
Penerusan mendeklarasikan enum adalah non-standar, karena pointer ke tipe enum yang berbeda tidak dijamin ukurannya sama. Compiler mungkin perlu melihat definisi untuk mengetahui ukuran pointer apa yang dapat digunakan dengan tipe ini.
Dalam praktiknya, setidaknya pada semua kompiler populer, pointer ke enum adalah ukuran yang konsisten. Deklarasi maju enum disediakan sebagai ekstensi bahasa oleh Visual C ++, misalnya.
sumber
Memang tidak ada yang namanya deklarasi maju enum. Karena definisi enum tidak mengandung kode apa pun yang bisa bergantung pada kode lain menggunakan enum, biasanya bukan masalah untuk mendefinisikan enum sepenuhnya ketika Anda pertama kali mendeklarasikannya.
Jika satu-satunya penggunaan enum Anda adalah dengan fungsi anggota pribadi, Anda dapat mengimplementasikan enkapsulasi dengan menjadikan enum itu sendiri sebagai anggota pribadi kelas itu. Enum masih harus sepenuhnya didefinisikan pada titik deklarasi, yaitu, dalam definisi kelas. Namun, ini bukan masalah yang lebih besar dengan mendeklarasikan fungsi anggota privat di sana, dan bukan eksposur internal implementasi yang lebih buruk dari itu.
Jika Anda membutuhkan tingkat penyembunyian yang lebih dalam untuk detail implementasi Anda, Anda dapat memecahnya menjadi antarmuka abstrak, hanya terdiri dari fungsi virtual murni, dan implementasi antarmuka (pewarisan) antarmuka yang nyata, sangat tersembunyi. Pembuatan instance kelas dapat ditangani oleh pabrik atau fungsi anggota statis dari antarmuka. Dengan begitu, bahkan nama kelas sebenarnya, apalagi fungsi privatnya, tidak akan diekspos.
sumber
Hanya mencatat bahwa alasan sebenarnya adalah bahwa ukuran enum belum diketahui setelah deklarasi maju. Nah, Anda menggunakan deklarasi maju dari sebuah struct untuk dapat melewati sebuah pointer di sekitar atau merujuk ke suatu objek dari tempat yang dirujuk dalam definisi struct yang dinyatakan maju itu sendiri juga.
Meneruskan mendeklarasikan enum tidak akan terlalu berguna, karena seseorang ingin dapat membagikan enum berdasarkan nilai. Anda bahkan tidak dapat memiliki pointer ke sana, karena saya baru-baru ini diberitahu beberapa platform menggunakan pointer ukuran yang berbeda untuk char daripada untuk int atau panjang. Jadi itu semua tergantung pada isi enum.
Standar C ++ saat ini secara eksplisit melarang melakukan sesuatu seperti
(dalam
7.1.5.3/1
). Tapi berikutnya C ++ Standar karena tahun depan memungkinkan berikut, yang meyakinkan saya masalah sebenarnya memiliki hubungannya dengan jenis yang mendasari:Ini dikenal sebagai deklarasi enum "buram". Anda bahkan dapat menggunakan X dengan nilai dalam kode berikut. Dan pencacahnya nanti dapat didefinisikan dalam deklarasi ulang pencacahan nanti. Lihat
7.2
di draft kerja saat ini.sumber
Saya akan melakukannya dengan cara ini:
[di tajuk publik]
[di tajuk internal]
Dengan menambahkan FORCE_32BIT, kami memastikan Econtent mengkompilasi ke panjang, sehingga dapat dipertukarkan dengan E.
sumber
Jika Anda benar-benar tidak ingin enum Anda muncul di file header Anda DAN memastikan bahwa itu hanya digunakan dengan metode pribadi, maka salah satu solusinya adalah dengan prinsip pimpl.
Ini adalah teknik yang memastikan untuk menyembunyikan internal kelas di header dengan hanya menyatakan:
Kemudian dalam file implementasi Anda (cpp), Anda mendeklarasikan kelas yang akan menjadi representasi internal.
Anda harus secara dinamis membuat implementasi di konstruktor kelas dan menghapusnya di destruktor dan ketika menerapkan metode publik, Anda harus menggunakan:
Ada pro untuk menggunakan pimpl, salah satunya adalah decouple header kelas Anda dari implementasinya, tidak perlu mengkompilasi ulang kelas lain saat mengubah implementasi satu kelas. Lain adalah mempercepat waktu kompilasi Anda karena header Anda sangat sederhana.
Tapi itu sulit digunakan, jadi Anda harus bertanya pada diri sendiri apakah hanya menyatakan enum sebagai pribadi di header adalah masalah besar.
sumber
Anda dapat membungkus enum dalam sebuah struct, menambahkan beberapa konstruktor dan mengetik konversi, dan meneruskan mendeklarasikan struct sebagai gantinya.
Tampaknya ini berfungsi: http://ideone.com/TYtP2
sumber
Tampaknya tidak dapat dideklarasikan ke depan dalam GCC!
Diskusi yang menarik di sini
sumber
Ada beberapa perbedaan pendapat karena ini terbentur (semacam), jadi inilah beberapa bit yang relevan dari standar. Penelitian menunjukkan bahwa standar tersebut tidak benar-benar mendefinisikan deklarasi maju, juga tidak secara eksplisit menyatakan bahwa enum dapat atau tidak dapat dinyatakan maju.
Pertama, dari dcl.enum, bagian 7.2:
Jadi, jenis enum yang mendasarinya adalah implementasi yang ditentukan, dengan satu batasan kecil.
Selanjutnya kita beralih ke bagian "tipe tidak lengkap" (3,9), yaitu hampir sedekat yang kita lihat dengan standar apa pun pada deklarasi maju:
Jadi disana, standarnya cukup banyak ditata jenis-jenis yang bisa dideklarasikan. Enum tidak ada di sana, jadi penulis kompiler umumnya menganggap forward menyatakan tidak diizinkan oleh standar karena ukuran variabel dari tipe yang mendasarinya.
Masuk akal juga. Enum biasanya dirujuk dalam situasi menurut nilai, dan kompiler memang perlu mengetahui ukuran penyimpanan dalam situasi tersebut. Karena ukuran penyimpanan adalah implementasi yang ditentukan, banyak kompiler hanya dapat memilih untuk menggunakan nilai 32 bit untuk tipe yang mendasari setiap enum, di mana pada saat itu dimungkinkan untuk meneruskannya. Eksperimen yang menarik mungkin adalah dengan mencoba mendeklarasikan enum di studio visual, kemudian memaksanya untuk menggunakan tipe dasar yang lebih besar dari sizeof (int) seperti yang dijelaskan di atas untuk melihat apa yang terjadi.
sumber
Untuk VC, inilah ujian tentang penerusan deklarasi dan menentukan tipe yang mendasarinya:
Tetapi mendapat peringatan untuk / W4 (/ W3 tidak dikenakan peringatan ini)
peringatan C4480: ekstensi tidak standar yang digunakan: menentukan tipe yang mendasari untuk enum 'T'
VC (Microsoft (R) 32-bit C / C ++ Mengoptimalkan Versi Kompiler 15.00.30729.01 untuk 80x86) terlihat bermasalah dalam kasus di atas:
Kode rakitan di atas diekstraksi dari /Fatest.asm secara langsung, bukan tebakan pribadi saya. Apakah Anda melihat mov DWORD PTR [eax], 305419896; Baris 12345678H?
cuplikan kode berikut membuktikannya:
hasilnya adalah: 0x78, 0x56, 0x34, 0x12
instruksi kunci di atas menjadi:
mov BYTE PTR [eax], 120; 00000078H
hasil akhirnya adalah: 0x78, 0x1, 0x1, 0x1
Perhatikan nilainya tidak ditimpa
Jadi penggunaan maju-deklarasi enum di VC dianggap berbahaya.
BTW, tidak mengherankan, sintaks untuk deklarasi tipe yang mendasarinya sama dengan di C #. Dalam praktiknya saya menemukan itu layak untuk menyimpan 3 byte dengan menentukan tipe yang mendasarinya sebagai char ketika berbicara dengan sistem tertanam, yang merupakan memori terbatas.
sumber
Dalam proyek saya, saya mengadopsi teknik Namespace-Bound Enumeration untuk menangani
enum
komponen warisan dan pihak ketiga. Berikut ini sebuah contoh:forward.h:
enum.h:
foo.h:
foo.cc:
main.cc:
Perhatikan bahwa
foo.h
tajuk tidak harus tahu apa-apalegacy::evil
. Hanya file yang menggunakan tipe lawaslegacy::evil
(di sini: main.cc) yang perlu disertakanenum.h
.sumber
Solusi saya untuk masalah Anda adalah:
1 - gunakan int bukan enum: Deklarasikan int Anda di ruang nama anonim di file CPP Anda (bukan di header):
Karena metode Anda bersifat pribadi, tidak ada yang akan mengacaukan data. Anda bahkan dapat melangkah lebih jauh untuk menguji apakah seseorang mengirimi Anda data yang tidak valid:
2: buat kelas penuh dengan instantiations const terbatas, seperti yang dilakukan di Jawa. Teruskan mendeklarasikan kelas, dan kemudian mendefinisikannya dalam file CPP, dan hanya instanciate nilai-nilai seperti enum. Saya melakukan sesuatu seperti itu di C ++, dan hasilnya tidak memuaskan seperti yang diinginkan, karena memerlukan beberapa kode untuk mensimulasikan enum (copy konstruksi, operator =, dll).
3: Seperti yang diusulkan sebelumnya, gunakan enum yang dideklarasikan secara pribadi. Terlepas dari kenyataan bahwa pengguna akan melihat definisi lengkapnya, ia tidak akan dapat menggunakannya, atau menggunakan metode pribadi. Jadi, Anda biasanya dapat memodifikasi enum dan konten metode yang ada tanpa perlu mengkompilasi ulang kode menggunakan kelas Anda.
Dugaan saya akan menjadi solusi 3 atau 1.
sumber
Karena enum dapat menjadi ukuran integral dari berbagai ukuran (kompiler memutuskan ukuran yang dimiliki oleh enum), penunjuk ke enum juga dapat memiliki ukuran yang bervariasi, karena ini adalah tipe integral (karakter memiliki pointer dari ukuran yang berbeda pada beberapa platform). misalnya).
Jadi kompiler bahkan tidak dapat membiarkan Anda meneruskan-mendeklarasikan enum dan pengguna pointer ke sana, karena bahkan di sana, ia membutuhkan ukuran enum.
sumber
Anda menentukan enumerasi untuk membatasi nilai elemen elemen yang mungkin untuk kumpulan terbatas. Pembatasan ini harus diberlakukan pada waktu kompilasi.
Ketika meneruskan menyatakan fakta bahwa Anda akan menggunakan 'set terbatas' nanti tidak menambah nilai: kode selanjutnya perlu mengetahui nilai yang mungkin untuk mendapatkan manfaat darinya.
Meskipun compiler yang bersangkutan tentang ukuran tipe enumerasi, yang maksud dari pencacahan akan hilang ketika Anda maju menyatakan hal itu.
sumber