Cara menggunakan enums di C ++

218

Misalkan kita memiliki yang enumseperti berikut:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};

Saya ingin membuat instance dari ini enumdan menginisialisasi dengan nilai yang tepat, jadi saya lakukan:

Days day = Days.Saturday;

Sekarang saya ingin memeriksa variabel atau instance saya dengan enumnilai yang ada , jadi saya lakukan:

if (day == Days.Saturday)
{
    std::cout << "Ok its Saturday";
}

Yang memberi saya kesalahan kompilasi:

galat: ekspresi primer yang diharapkan sebelum '.' token

Jadi untuk menjadi jelas, apa perbedaan antara mengatakan:

if (day == Days.Saturday) // Causes compilation error

dan

if (day == Saturday)

?

Apa yang sebenarnya dimaksud oleh kedua hal ini, di mana yang OK dan yang satu menyebabkan kesalahan kompilasi?

Rika
sumber
4
saya tahu, saya ingin tahu mengapa ini memberi saya kesalahan!
Rika
1
Hari Rabu di sini. Anda memiliki terlalu banyak kesalahan sintaks untuk kompiler C ++. Mulai dari 'Enum'.
Öö Tiib
1
@ Hussein, Karena enums bukan sintaks yang sama (dan semantik) dalam kedua bahasa. Hal pertama yang saya lakukan setelah mendapatkan kesalahan ketika mencoba menggunakan fitur dalam bahasa baru adalah mencari sintaks (atau jika mungkin) dalam bahasa itu.
chris
@ Chris: Saya tahu, saya melakukan hal yang persis sama. Semoga saya mendapat jawaban saya. Saya juga memperbarui pertanyaannya menjadi lebih jelas. Terima kasih;)
Rika
17
" Sejauh yang saya tahu deklarasi enums dan penggunaan dalam dua bahasa ini adalah sama. " Itu masalahnya, di sana. C # bukan bahasa yang sama dengan C ++. Khususnya, mereka memiliki sintaks yang berbeda untuk enum.
Robᵩ

Jawaban:

350

Kode ini salah:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Days.Saturday;
if (day == Days.Saturday)

Karena Daysbukan ruang lingkup, atau objek. Itu adalah tipe. Dan Tipe sendiri tidak memiliki anggota. Apa yang Anda tulis sama dengan std::string.clear. std::stringadalah tipe, jadi Anda tidak dapat menggunakannya .. Anda menggunakan .pada contoh dari sebuah kelas.

Sayangnya, enum itu ajaib dan analoginya berhenti sampai di situ. Karena dengan kelas, Anda bisa melakukan std::string::clearuntuk mendapatkan pointer ke fungsi anggota, tetapi di C ++ 03, Days::Sundaytidak valid. (Yang menyedihkan). Ini karena C ++ (agak) mundur kompatibel dengan C, dan C tidak memiliki ruang nama, jadi enumerasi harus berada dalam ruang nama global. Jadi sintaksnya sederhana:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday)

Untungnya, Mike Seymour mengamati bahwa ini telah dibahas dalam C ++ 11. Ubah enumke enum classdan itu mendapatkan ruang lingkupnya sendiri; jadi Days::Sundaytidak hanya valid, tetapi merupakan satu - satunya cara untuk mengakses Sunday. Hari hari menyenangkan!

Mooing Duck
sumber
254
Untungnya, keluhan Anda telah diatasi di C ++ 11. Ubah enumke enum classdan itu mendapatkan ruang lingkupnya sendiri; jadi Days::Sundaytidak hanya valid, tetapi merupakan satu-satunya cara untuk mengakses Sunday. Hari hari menyenangkan!
Mike Seymour
11
Harus menyukai pesan kesalahan C ++ ... mereka membuktikan bahwa bahasa tersebut rumit bahkan untuk memberikan umpan balik yang baik. Saya menganggapnya sebagai 'ekspresi primer' adalah objek atau ruang lingkup atau beberapa hal lain yang BUKAN tipe. Mungkin Type adalah 'ekspresi sekunder'. Dan apa yang disebut pengembang C ++ sebagai 'operator titik', kompiler C ++ hanya dapat memanggil 'token'. Ketika menjadi sulit untuk memahami pesan kesalahan ada sesuatu yang salah dengan bahasa yang saya pikir.
Travis
4
@ Travis: en.cppreference.com/w/cpp/language/… . Ekspresi primer hanyalah hal pertama dalam sebuah ekspresi, biasanya nama atau variabel atau literal. Adapun bagian kedua, saya tidak melihat perbedaan besar antara '.' tokendan dot operator, selain itu token dan bukan operator, dan itu menunjukkan simbol yang tepat, bukan nama.
Mooing Duck
@ Mike Seymour Saya sudah mencoba mengakses enum tanpa operator resolusi lingkup pada sekelompok kompiler, dan sepertinya berhasil. Anda mengatakan pada C ++ 11 itu satu-satunya cara, untuk beberapa alasan saya hanya dapat mengakses nilai enum sebagai global, tidak perlu ::
Zebrafish
1
@TitoneMaurice: Jika Anda memiliki enum, Anda tidak dapat menggunakan cakupan, atau lingkup global ( ::Saturday). Jika Anda memiliki enum class(yang merupakan hal yang sangat berbeda), maka Anda harus menggunakannya Days::Saturday.
Mooing Duck
24

Ini akan cukup untuk mendeklarasikan variabel enum Anda dan membandingkannya:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday) {
    std::cout << "Ok its Saturday";
}
ahli matematika1975
sumber
mengapa salah mengatakan jika (hari == Days.Satudday)? mereka harus sama, jadi mengapa kompiler mengeluh tentang hal itu?
Rika
1
@ Memiliki nilai-nilai yang dinyatakan dalam enum Anda tidak berperilaku seperti variabel anggota kelas atau struct. Ini bukan sintaks yang tepat untuk digunakan
mathematician1975
2
@ Hussein: karena Daysbukan ruang lingkup, atau objek. Itu adalah tipe. Dan Tipe sendiri tidak memiliki anggota. std::string.clearjuga gagal dikompilasi karena alasan yang sama.
Mooing Duck
8
@ Hussein: Karena itu bukan cara enums di C ++ bekerja. Pencacahan yang tidak diselubungi memasukkan nilainya ke dalam namespace di sekitarnya; enum classruang lingkup ( , baru pada tahun 2011) memiliki cakupannya sendiri, dan diakses menggunakan operator lingkup Days::Saturday,. Operator akses anggota ( .) hanya digunakan untuk mengakses anggota kelas.
Mike Seymour
@MooingDUck dan MikeSeymour Apakah salah satu dari kalian akan memposting jawaban Anda sebagai jawaban? karena itulah yang saya cari setelah mengeluarkan pertanyaan ini;)
Rika
22

Banyak dari ini seharusnya memberi Anda kesalahan kompilasi.

// note the lower case enum keyword
enum Days { Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday };

Sekarang, Saturday, Sunday, dll dapat digunakan sebagai konstanta telanjang tingkat atas, dan Daysdapat digunakan sebagai tipe:

Days day = Saturday;   // Days.Saturday is an error

Dan juga nanti, untuk menguji:

if (day == Saturday)
    // ...

Ini enumnilai-nilai seperti konstanta telanjang - mereka un -scoped - dengan sedikit bantuan ekstra dari compiler: (kecuali jika Anda menggunakan C ++ 11 kelas enum ) mereka tidak dikemas seperti objek atau struktur anggota misalnya, dan Anda tidak dapat merujuk kepada mereka sebagai anggota dari Days.

Anda akan mendapatkan apa yang Anda cari dengan C ++ 11 , yang memperkenalkan enum class:

enum class Days
{
    SUNDAY,
    MONDAY,
    // ... etc.
}

// ...

if (day == Days::SUNDAY)
    // ...

Perhatikan bahwa C ++ ini sedikit berbeda dari C dalam beberapa cara, salah satunya adalah bahwa C membutuhkan penggunaan enumkata kunci saat mendeklarasikan variabel:

// day declaration in C:
enum Days day = Saturday;
pb2q
sumber
Saya telah memperbarui pertanyaan, saya pikir sekarang lebih jelas apa yang saya inginkan setelahnya :) Ngomong-ngomong, terima kasih :)
Rika
14

Anda dapat menggunakan trik untuk menggunakan cakupan sesuai keinginan, cukup deklarasikan enum dengan cara:

struct Days 
{
   enum type
   {
      Saturday,Sunday,Tuesday,Wednesday,Thursday,Friday
   };
};

Days::type day = Days::Saturday;
if (day == Days::Saturday)
ataman1x
sumber
9

Daripada menggunakan banyak pernyataan if, enum cocokkan diri mereka untuk berganti pernyataan

Saya menggunakan beberapa kombinasi enum / switch di pembangun tingkat yang saya bangun untuk permainan saya.

EDIT: Hal lain, saya melihat Anda ingin sintaks mirip dengan;

if(day == Days.Saturday)
etc

Anda dapat melakukan ini di C ++:

if(day == Days::Saturday)
etc

Ini adalah contoh yang sangat sederhana:

EnumAppState.h

#ifndef ENUMAPPSTATE_H
#define ENUMAPPSTATE_H
enum eAppState
{
    STARTUP,
    EDIT,
    ZONECREATION,
    SHUTDOWN,
    NOCHANGE
};
#endif

Somefile.cpp

#include "EnumAppState.h"
eAppState state = eAppState::STARTUP;
switch(state)
{
case STARTUP:
    //Do stuff
    break;
case EDIT:
    //Do stuff
    break;
case ZONECREATION:
    //Do stuff
    break;
case SHUTDOWN:
    //Do stuff
    break;
case NOCHANGE:
    //Do stuff
    break;
}
Dean Knight
sumber
Yang menyenangkan di sini adalah bahwa kompiler akan memberi tahu Anda jika Anda melewatkan memasukkan kasing.
chris
Bukankah Anda seharusnya menggunakan class enum dalam hal ini?
Rika
1
enum hanyalah tipe data dalam C ++ Jadi, mendeklarasikan enum seperti yang saya lakukan di atas dalam file .h, dan kemudian memasukkan file itu dalam file .cpp apa pun yang ingin Anda gunakan akan memberikan Anda akses ke enum. Hanya perhatikan saya lupa menambahkan #include dalam contoh .cpp saya. Editing.
Dean Knight
Juga, saya melihat orang lain mengatakan bahwa enum di C ++ bersifat global. Dalam pengalaman saya, menggunakan enum seperti yang saya miliki di atas, saya hanya bisa mengaksesnya ketika saya sudah memasukkan .h. Jadi ini sepertinya menghentikan akses global juga, yang selalu bagus. EDIT: Sepertinya saya tanpa sadar menggunakan enum dalam cara C ++ 11 jika saya membaca sesuatu dengan benar ...
Dean Knight
9

Jika Anda masih menggunakan C ++ 03 dan ingin menggunakan enums, Anda harus menggunakan enums di dalam namespace. Misalnya:

namespace Daysofweek{
enum Days {Saturday, Sunday, Tuesday,Wednesday, Thursday, Friday};
}

Anda dapat menggunakan enum di luar namespace seperti,

Daysofweek::Days day = Daysofweek::Saturday;

if (day == Daysofweek::Saturday)
{
    std::cout<<"Ok its Saturday";
}
San
sumber
8

Anda mencari enumerasi yang sangat diketik , fitur yang tersedia dalam standar C ++ 11 . Itu mengubah enumerasi menjadi kelas dengan nilai lingkup.

Menggunakan contoh kode Anda sendiri, itu adalah:

  enum class Days {Saturday, Sunday, Tuesday,Wednesday, Thursday, Friday};
  Days day = Days::Saturday;

  if (day == Days::Saturday)  {
    cout << " Today is Saturday !" << endl;
  }
  //int day2 = Days::Sunday; // Error! invalid

Menggunakan ::sebagai pengakor enumerasi akan gagal jika menargetkan standar C ++ sebelum C ++ 11. Tetapi beberapa kompiler lama tidak mendukungnya, juga beberapa IDE hanya mengganti opsi ini, dan menetapkan C ++ std yang lama.

Jika Anda menggunakan GCC, aktifkan C + 11 dengan -std = c ++ 11 atau -std = gnu11 .

Berbahagialah!

Alex Byrth
sumber
1
Anda lupa menulis enum class Days { ....
Martin Hennings
Memang. memperbaikinya! Terima kasih.
Alex Byrth
7

Ini seharusnya tidak berfungsi di C ++:

Days.Saturday

Hari bukanlah ruang lingkup atau objek yang berisi anggota yang dapat Anda akses dengan operator titik. Sintaks ini hanya sebuah C # -ism dan tidak sah di C ++.

Microsoft telah lama memelihara ekstensi C ++ yang memungkinkan Anda untuk mengakses pengidentifikasi menggunakan operator lingkup:

enum E { A, B, C };

A;
E::B; // works with Microsoft's extension

Tapi ini non-standar sebelum C ++ 11. Dalam C ++ 03 pengidentifikasi yang dideklarasikan dalam enum hanya ada dalam lingkup yang sama dengan tipe enum itu sendiri.

A;
E::B; // error in C++03

C ++ 11 membuatnya sah untuk memenuhi syarat pengidentifikasi enum dengan nama enum, dan juga memperkenalkan kelas enum, yang menciptakan ruang lingkup baru untuk pengidentifikasi alih-alih menempatkan mereka dalam lingkup sekitarnya.

A;
E::B; // legal in C++11

enum class F { A, B, C };

A; // error
F::B;
bames53
sumber
4

Sayangnya, elemen enum bersifat 'global'. Anda mengaksesnya dengan melakukan day = Saturday. Itu berarti bahwa Anda tidak dapat memiliki enum A { a, b } ;dan enum B { b, a } ;karena mereka berada dalam konflik.

Grzegorz
sumber
2
Sampai Anda gunakan enum classdi C ++ 11, yaitu. Sebelum itu, Anda harus membuat kelas dummy.
chris
Tidak tahu C ++ 11. Saya mengasumsikan pertanyaan mengacu pada C ++. Ya, menggunakan kelas atau ruang nama akan membantu.
Grzegorz
@ Grzegorz: saya pikir chris merujuk ke kelas enum yang baru diperkenalkan yang menyediakan enum yang sangat diketik.
Rika
@ Hussein: Terima kasih telah menunjukkannya. Saya telah menemukan penjelasan tentang num class, dan saya tahu apa yang dibicarakan Chris. Terima kasih banyak.
Grzegorz
@ Grzegorz: Saya tidak bermaksud untuk tidak hormat, hanya berpikir saya mungkin bisa membantu, maaf atas kesalahpahaman yang mungkin terjadi. Saya lagi Terima kasih atas waktu Anda dan membantu saya;)
Rika
4

Sementara C ++ (tidak termasuk C ++ 11) memiliki enum, nilai-nilai di dalamnya "bocor" ke namespace global.
Jika Anda tidak ingin bocor (dan jangan PERLU menggunakan tipe enum), pertimbangkan hal berikut:

class EnumName {  
   public:   
      static int EnumVal1;  
      (more definitions)  
};  
EnumName::EnumVal1 = {value};  
if ([your value] == EnumName::EnumVal1)  ...
Inisialisasi Sabah
sumber
3

Enum di C ++ seperti integer yang ditutupi oleh nama yang Anda berikan, ketika Anda mendeklarasikan nilai enum Anda (ini bukan definisi hanya petunjuk bagaimana cara kerjanya).

Tetapi ada dua kesalahan dalam kode Anda:

  1. Eja enumsemua huruf kecil
  2. Anda tidak perlu Days.sebelum hari Sabtu.
  3. Jika enum ini dideklarasikan di kelas, maka gunakan if (day == YourClass::Saturday){}
Balázs Édes
sumber
OP mengubah ejaan / kasing 16 menit setelah posting awal ( revisi 1 ke revisi 2 ).
Peter Mortensen
1

Saya pikir masalah root Anda adalah penggunaan .alih-alih ::, yang akan menggunakan namespace.

Mencoba:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Days::Saturday;
if(Days::Saturday == day)  // I like literals before variables :)
{
    std::cout<<"Ok its Saturday";
}
James Oravec
sumber
Ini tidak berfungsi: untuk menggunakan Days::ruang lingkup seperti dalam contoh Anda, Anda harus menentukan enumerasi dengan enum class Daysdan menggunakan C ++ 03 + ekstensi Microsoft atau C ++ 11.
Futal
@Futal, yang di atas berjalan dengan Borland C ++ Builder. Rasa / Versi C ++ tidak ada dalam pertanyaan.
James Oravec
1
versi Borland C ++ Builder Anda harus menggunakan C ++ 11 atau lebih baru. Gcc dan Dentang keduanya memberikan kesalahan atau peringatan jika contoh Anda dikompilasi dengan -std=c++98atau -std=c++03. Dentang cukup jelas: warning: use of enumeration in a nested name specifier is a C++11 extension.
Futal
1

Jika kita menginginkan keamanan tipe yang ketat dan lingkup enum, penggunaannya enum classbagus di C ++ 11.

Jika kami harus bekerja di C ++ 98, kami dapat menggunakan saran yang diberikan oleh InitializeSahib, Sanuntuk mengaktifkan enum yang dicakup.

Jika kita juga menginginkan keamanan tipe yang ketat, kode tindak dapat mengimplementasikan sesuatu seperti enum.

#include <iostream>
class Color
{
public:
    static Color RED()
    {
        return Color(0);
    }
    static Color BLUE()
    {
        return Color(1);
    }
    bool operator==(const Color &rhs) const
    {
        return this->value == rhs.value;
    }
    bool operator!=(const Color &rhs) const
    {
        return !(*this == rhs);
    }

private:
    explicit Color(int value_) : value(value_) {}
    int value;
};

int main()
{
    Color color = Color::RED();
    if (color == Color::RED())
    {
        std::cout << "red" << std::endl;
    }
    return 0;
}

Kode dimodifikasi dari contoh Bulan kelas di buku Efektif C ++ 3: Butir 18

Xu Hui
sumber
-15

Pertama-tama, buat 'E' di enum, 'e' sebagai huruf kecil.

Kedua, masukkan nama tipe 'Days' di 'Days.Saturday'.

Ketiga ... beli sendiri buku C ++ yang bagus.

vikramjitSingh
sumber
5
Maaf Anda mendapatkan semua suara buruk ini (maksud saya, jawabannya memang pantas untuk itu), tetapi itu tidak berarti Anda harus meninggalkan komunitas selama 6 tahun. Kembali dan bergabunglah dengan kami. Anda memiliki sesuatu untuk disumbangkan juga. Berguna. Berbagi pengetahuan.
Gabriel Staples