Struct menyelesaikan masalah mengelompokkan data; masalah berikutnya adalah data yang hanya boleh berisi pilihan tertentu. Status pesanan hanya boleh “menunggu”, “dikirim”, atau “selesai”; warna lampu hanya boleh merah, kuning, atau hijau. Menyimpannya sebagai int (0 = menunggu, 1 = dikirim, …) membuat kode penuh angka tanpa makna — dan pemula maupun kita sendiri pasti pernah lupa bahwa angka 2 berarti “selesai”. C++ menyediakan enum (enumeration) untuk membuat konstanta bernama yang terbatas pada daftar pilihan. Pada Chapter ini kita menulis enum unscoped, mengatur nilainya secara implisit maupun eksplisit, lalu mengamati dua kelemahannya yang menjadi alasan fitur enum class lahir di chapter berikutnya.
Enum sebagai Konstanta Bernama #
Deklarasi enum dimulai dengan kata kunci enum, diikuti nama tipenya, lalu daftar enumerator di dalam kurung kurawal. Setiap enumerator menjadi konstanta yang dapat dipakai di seluruh kode, dan variabel bertipe enum itu hanya diharapkan berisi salah satu enumerator — tipe enum memberi “kosakata” khusus pada domain masalah kita. Dibanding sekelompok const int, enum menggabungkan konstanta-konstanta itu di bawah satu nama tipe sehingga maksudnya terdokumentasi sendiri.
#include <iostream>
// Unscoped enum: nama enumerator bocor ke scope di sekitarnya
enum Warna { Merah, Hijau, Biru }; // nilai implisit: 0, 1, 2
// Nilai eksplisit: cocok untuk kode protokol atau konstanta domain
enum Level {
Rendah = 1,
Sedang = 5,
Tinggi = 10
};
void kerjakan(Level lvl) {
if (lvl == Tinggi) {
std::cout << "Prioritas tinggi, kerjakan dulu\n";
} else {
std::cout << "Diantrikan dengan level " << lvl << "\n";
}
}
int main() {
Warna favorit = Hijau;
std::cout << "Hijau bernilai " << Hijau << "\n"; // 1
std::cout << "Level: " << Rendah << ", " << Sedang << ", " << Tinggi << "\n";
if (favorit == Biru) {
std::cout << "Favorit: biru\n";
} else {
std::cout << "Favorit bukan biru\n";
}
kerjakan(Sedang);
// Konversi implisit ke int: berjalan tanpa suara
int skor = Tinggi;
std::cout << "Skor: " << skor << "\n";
return 0;
}Output yang Dihasilkan:
Hijau bernilai 1
Level: 1, 5, 10
Favorit bukan biru
Diantrikan dengan level 5
Skor: 10Mari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 4 (
enum Warna { Merah, Hijau, Biru };): enum tanpa nilai eksplisit. Compiler memberi nilai implisit berurutan dari nol:Merah= 0,Hijau= 1,Biru= 2 — terbukti saatHijaudicetak menjadi1. - Baris 7-11 (
enum Level { Rendah = 1, ... }): nilai eksplisit ditentukan dengan=. Enumerator berikutnya yang tidak diberi nilai akan melanjutkan deretnya (satu di atas nilai sebelumnya); di sini ketiganya diatur manual agar sesuai kode protokol yang diinginkan. - Baris 13-19 (
void kerjakan(Level lvl)): enum menjadi tipe parameter yang jelas maksudnya — fungsi menuntutLevel, bukanintsembarangan. Pembandinganlvl == Tinggibekerja antar enumerator. - Baris 22 (
Warna favorit = Hijau;): variabel bertipe enum diisi enumerator. Pembandinganfavorit == Birudievaluasi compiler tanpa konversi apa pun karena keduanya satu tipe. - Baris 36-37 (
int skor = Tinggi;): inilah titik lemah yang akan kita bahas — enum unscope diterimainttanpa suara, sehinggaskorberisi 10.
Nilai Implisit dan Eksplisit #
Kapan memakai yang mana? Nilai implisit (biarkan compiler menghitung 0, 1, 2, …) tepat ketika urutan tidak perlu cocok dengan sesuatu di luar program — enum hanya dipakai sebagai label di dalam kode. Nilai eksplisit wajib ketika nilai enum harus cocok dengan dunia luar: kode status perangkat keras, opcode protokol jaringan, atau format file yang didokumentasikan dengan angka tetap. Di antara keduanya ada pola campuran yang juga umum:
enum Ukuran { Kecil, Sedang, Besar, Kustom = 100 };
// Kecil = 0, Sedang = 1, Besar = 2, Kustom = 100
Di sini Kustom tidak melanjutkan deret 3, melainkan melompat ke 100 sesuai penugasan kita. Satu enum juga boleh berisi dua enumerator dengan nilai sama (misalnya Nol = 0 dan Kosong = 0), sah menurut standar — tapi hindari bila tidak ada alasan domain yang kuat, karena pembandingan if (x == Nol) akan lolos untuk Kosong juga.
Masalah Unscoped Enum #
Nama resminya unscoped enum karena enumerator-nya tidak punya scope sendiri: Merah, Hijau, dan Biru langsung berhuni di scope di sekitarnya, setara dengan variabel global. Konsekuensi pertamanya adalah tabrakan nama. Dua enum yang sama-sama punya enumerator Merah tidak bisa hidup berdampingan:
enum Warna { Merah, Hijau, Biru };
enum Buah { Apel, Mangga, Merah }; // Merah sudah dipakai enum lain
Compiler menolaknya dengan pesan yang jelas:
error: 'Merah' conflicts with a previous declarationBahkan nama enumerator bertabrakan dengan variabel biasa: mendeklarasikan int Hijau = 7; setelah enum di atas menghasilkan error: 'int Hijau' redeclared as different kind of entity. Semakin besar proyek, semakin mungkin dua konstanta bernama sama muncul — dan unscoped enum membuat kita bergantung pada keberuntungan penamaan.
Konsekuensi kedua adalah konversi implisit ke int. Enum unscoped diterima di mana-mana yang mengharapkan int, tanpa peringatan apa pun:
Warna favorit = Hijau;
if (favorit == 1) { /* lolos compiler: Hijau memang bernilai 1 */ }
int n = favorit; // sah tanpa suara, n berisi 1
Baris favorit == 1 terbaca logis, tetapi pembandingan lintas makna seperti itu menyembunyikan bug: angka 1 berasal dari mana? Jika suatu saat urutan enum diubah, semua pembandingan angka hancur diam-diam. Arah sebaliknya — menugaskan int ke enum — memang ditolak compiler dengan pesan invalid conversion from 'int' to 'Warna', tetapi itu kecil menolong saat int yang menyamar sebagai enum terus lolos begitu saja. C++ membiarkan konversi enum-ke-int ini demi kompatibilitas dengan C, lalu komite standar menyediakan obatnya: scoped enum yang tidak bocor, tidak berkonversi bebas, dan dieja enum class.
Referensi resmi untuk enumerasi dapat dibaca pada halaman enum
di cppreference. Chapter berikutnya memperkenalkan enum class secara khusus: sintaksnya, cara mengeluarkan nilainya secara eksplisit, dan mengapa ia menjadi pilihan utama untuk kode modern.