Enumerasi (C++)
Enumerasi adalah jenis yang ditentukan pengguna yang terdiri dari sekumpulan konstanta integral bernama yang dikenal sebagai enumerator.
Catatan
Artikel ini membahas jenis Bahasa enum
C++ Standar ISO dan jenis cakupan (atau sangat diketik) enum class
yang diperkenalkan dalam C++11. Untuk informasi tentang jenis atau private enum class
di C++/CLI dan C++/CX, lihat enum class
(C++/CLI dan C++/CX).public enum class
Sintaks
enum-name
:
identifier
enum-specifier
:
enum-head
{
enumerator-list
Memilih }
enum-head
{
enumerator-list
,
}
enum-head
:
enum-key
attribute-specifier-seq
opt enum-head-name
opt enum-base
opt
enum-head-name
:
nested-name-specifier
Memilih identifier
opaque-enum-declaration
:
enum-key
attribute-specifier-seq
opt opt enum-head-name
enum-base
;
enum-key
:
enum
enum class
enum struct
enum-base
:
:
type-specifier-seq
enumerator-list
:
enumerator-definition
enumerator-list
,
enumerator-definition
enumerator-definition
:
enumerator
enumerator
=
constant-expression
enumerator
:
identifier
attribute-specifier-seq
Memilih
Penggunaan
// unscoped enum:
// enum [identifier] [: type] {enum-list};
// scoped enum:
// enum [class|struct] [identifier] [: type] {enum-list};
// Forward declaration of enumerations (C++11):
enum A : int; // non-scoped enum must have type specified
enum class B; // scoped enum defaults to int but ...
enum class C : short; // ... may have any integral underlying type
Parameter
identifier
Nama jenis yang diberikan ke enumerasi.
type
Jenis enumerator yang mendasar; semua enumerator memiliki jenis yang mendasar yang sama. Mungkin jenis integral apa pun.
enum-list
Daftar enumerator yang dipisahkan koma dalam enumerasi. Setiap enumerator atau nama variabel dalam cakupan harus unik. Namun, nilai dapat diduplikasi. Dalam enum yang tidak terlingkup, cakupannya adalah cakupan di sekitarnya; dalam enum terlingkup, cakupannya adalah itu enum-list
sendiri. Dalam enum terlingkup, daftar mungkin kosong, yang berlaku mendefinisikan jenis integral baru.
class
Dengan menggunakan kata kunci ini dalam deklarasi, Anda menentukan enum dilingkup, dan identifier
harus disediakan. Anda juga dapat menggunakan struct
kata kunci sebagai ganti class
, karena kata kunci tersebut setara secara semantik dalam konteks ini.
Cakupan enumerator
Enumerasi menyediakan konteks untuk menjelaskan rentang nilai yang direpresentasikan sebagai konstanta bernama. Konstanta bernama ini juga disebut enumerator. Dalam jenis C dan C++ enum
asli, enumerator yang tidak memenuhi syarat terlihat di seluruh cakupan tempat enum
dideklarasikan. Dalam enum terlingkup, nama enumerator harus memenuhi syarat dengan enum
nama jenis. Contoh berikut menunjukkan perbedaan dasar ini antara dua jenis enum:
namespace CardGame_Scoped
{
enum class Suit { Diamonds, Hearts, Clubs, Spades };
void PlayCard(Suit suit)
{
if (suit == Suit::Clubs) // Enumerator must be qualified by enum type
{ /*...*/}
}
}
namespace CardGame_NonScoped
{
enum Suit { Diamonds, Hearts, Clubs, Spades };
void PlayCard(Suit suit)
{
if (suit == Clubs) // Enumerator is visible without qualification
{ /*...*/
}
}
}
Setiap nama dalam enumerasi diberi nilai integral yang sesuai dengan tempatnya dalam urutan nilai dalam enumerasi. Secara default, nilai pertama ditetapkan 0, yang berikutnya ditetapkan 1, dan seterusnya, tetapi Anda dapat secara eksplisit mengatur nilai enumerator, seperti yang ditunjukkan di sini:
enum Suit { Diamonds = 1, Hearts, Clubs, Spades };
Enumerator Diamonds
diberi nilai 1
. Enumerator berikutnya, jika mereka tidak diberi nilai eksplisit, terima nilai enumerator sebelumnya ditambah satu. Dalam contoh sebelumnya, Hearts
akan memiliki nilai 2, Clubs
akan memiliki 3, dan sebagainya.
Setiap enumerator diperlakukan sebagai konstanta dan harus memiliki nama unik dalam cakupan di mana enum
didefinisikan (untuk enum yang tidak terlingkup) atau di dalamnya enum
sendiri (untuk enum cakupan). Nilai yang diberikan untuk nama tidak harus unik. Misalnya, pertimbangkan deklarasi enum Suit
yang tidak terlingkup ini:
enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };
Diamonds
Nilai , , Hearts
Clubs
, dan Spades
masing-masing adalah 5, 6, 4, dan 5. Perhatikan bahwa 5 digunakan lebih dari sekali; itu diizinkan meskipun mungkin tidak dimaksudkan. Aturan ini sama untuk enum terlingkup.
Aturan transmisi
Konstanta enum yang tidak terlingkup dapat dikonversi secara implisit ke int
, tetapi int
tidak pernah secara implisit dapat dikonversi ke nilai enum. Contoh berikut menunjukkan apa yang terjadi jika Anda mencoba menetapkan hand
nilai yang bukan :Suit
int account_num = 135692;
Suit hand;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
Transmisi diperlukan untuk mengonversi int
ke enumerator terlingkup atau tidak terlingkup. Namun, Anda dapat mempromosikan enumerator yang tidak terlingkup ke nilai bilangan bulat tanpa transmisi.
int account_num = Hearts; //OK if Hearts is in an unscoped enum
Menggunakan konversi implisit dengan cara ini dapat menyebabkan efek samping yang tidak diinginkan. Untuk membantu menghilangkan kesalahan pemrograman yang terkait dengan enum yang tidak terlingkup, nilai enum terlingkup sangat di ketik. Enumerator terlingkup harus memenuhi syarat dengan nama jenis enum (pengidentifikasi) dan tidak dapat dikonversi secara implisit, seperti yang ditunjukkan dalam contoh berikut:
namespace ScopedEnumConversions
{
enum class Suit { Diamonds, Hearts, Clubs, Spades };
void AttemptConversions()
{
Suit hand;
hand = Clubs; // error C2065: 'Clubs' : undeclared identifier
hand = Suit::Clubs; //Correct.
int account_num = 135692;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'
hand = static_cast<Suit>(account_num); // OK, but probably a bug!!!
account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int'
account_num = static_cast<int>(Suit::Hearts); // OK
}
}
Perhatikan bahwa baris hand = account_num;
masih menyebabkan kesalahan yang terjadi dengan enum yang tidak terlingkup, seperti yang ditunjukkan sebelumnya. Ini diperbolehkan dengan pemeran eksplisit. Namun, dengan enum terlingkup, konversi yang dicoba dalam pernyataan berikutnya, account_num = Suit::Hearts;
, tidak lagi diizinkan tanpa pemeran eksplisit.
Enum tanpa enumerator
Visual Studio 2017 versi 15.3 dan yang lebih baru (Tersedia dengan /std:c++17
dan yang lebih baru): Dengan mendefinisikan enum (reguler atau tercakup) dengan jenis yang mendasar eksplisit dan tidak ada enumerator, Anda dapat memperkenalkan jenis integral baru yang tidak memiliki konversi implisit ke jenis lain. Dengan menggunakan jenis ini alih-alih jenis dasar bawaannya, Anda dapat menghilangkan potensi kesalahan halus yang disebabkan oleh konversi implisit yang tidak disengaja.
enum class byte : unsigned char { };
Jenis baru adalah salinan yang tepat dari jenis yang mendasar, dan oleh karena itu memiliki konvensi panggilan yang sama, yang berarti dapat digunakan di seluruh ABI tanpa penalti performa. Tidak ada transmisi yang diperlukan ketika variabel jenis diinisialisasi dengan menggunakan inisialisasi daftar langsung. Contoh berikut menunjukkan cara menginisialisasi enum tanpa enumerator dalam berbagai konteks:
enum class byte : unsigned char { };
enum class E : int { };
E e1{ 0 };
E e2 = E{ 0 };
struct X
{
E e{ 0 };
X() : e{ 0 } { }
};
E* p = new E{ 0 };
void f(E e) {};
int main()
{
f(E{ 0 });
byte i{ 42 };
byte j = byte{ 42 };
// unsigned char c = j; // C2440: 'initializing': cannot convert from 'byte' to 'unsigned char'
return 0;
}