Bisakah kelas C ++ enum memiliki metode?

145

Saya memiliki kelas enum dengan dua nilai, dan saya ingin membuat metode yang menerima nilai dan mengembalikan yang lainnya. Saya juga ingin menjaga keamanan tipe (itu sebabnya saya menggunakan kelas enum bukan enum).

http://www.cplusplus.com/doc/tutorial/other_data_types/ tidak menyebutkan apa pun tentang metode Namun, saya mendapat kesan bahwa semua jenis kelas dapat memiliki metode.

oktavian
sumber
4
Tidak, tidak bisa. Lihat di sini .
juanchopanza
@octavian Perhatikan jawaban saya dan pikirkan kembali tentang kasus penggunaan Anda!
πάντα ῥεῖ
@ πάνταῥεῖ Anda sepenuhnya benar, saya sudah membaca enum tetapi berpikir penyatuan, membunuh komentar.
Eugen Constantin Dinca
@octavian Apakah Anda bahkan meminta kasus penggunaan tertentu, atau apakah Anda hanya ingin agar pembatasan standar pada c ++ 11 enum class/struct dikonfirmasi?
πάντα ῥεῖ
Saya memiliki kegunaan dalam pikiran ... dan ini adalah masalah mendasar
oktavian

Jawaban:

118

Tidak, mereka tidak bisa.

Saya dapat memahami bahwa enum classbagian untuk enums yang sangat diketik dalam C ++ 11 mungkin tampak menyiratkan bahwa Anda enummemiliki classsifat juga, tetapi bukan itu masalahnya. Dugaan saya yang berpendidikan adalah bahwa pilihan kata kunci terinspirasi oleh pola yang kami gunakan sebelum C ++ 11 untuk mendapatkan cakupan enum:

class Foo {
public:
  enum {BAR, BAZ};
};

Namun, itu hanya sintaks. Sekali lagi, enum classbukan a class.

Stefano Sanfilippo
sumber
88
Pada ## C ++ saya diberi tahu bahwa "c ++ bertujuan untuk menjadi membingungkan dan seramah mungkin" . Jelas ini sebuah lelucon, tetapi Anda mendapatkan idenya :)
Stefano Sanfilippo
4
A unionbukan apa yang John Doe akan anggap sebagai kelas juga. Namun mereka dapat memiliki fungsi anggota. Dan kelas benar-benar tidak wajib untuk fungsi anggota. Menggunakan penunjuk suka valueatau this, sesuatu seperti enum Size { Huge, Mega, Apocalypse; bool operator<(X rhs) const { return *this < rhs; }(di sini juga memungkinkan ;), itu bisa masuk akal seperti bentuk-bentuk fungsi lainnya.
Sebastian Mach
85

Sementara jawaban yang "Anda tidak bisa" secara teknis benar, saya percaya Anda mungkin dapat mencapai perilaku yang Anda cari menggunakan ide berikut:

Saya membayangkan Anda ingin menulis sesuatu seperti:

Fruit f = Fruit::Strawberry;
f.IsYellow();

Dan Anda berharap kode tersebut terlihat seperti ini:

enum class Fruit : uint8_t
{
  Apple, 
  Pear,
  Banana,
  Strawberry,

  bool IsYellow() { return this == Banana; }
};

...

Tetapi tentu saja, itu tidak berhasil, karena enum tidak dapat memiliki metode (dan 'ini' tidak berarti apa-apa dalam konteks di atas)

Namun, jika Anda menggunakan gagasan kelas normal yang berisi enum non-kelas dan variabel anggota tunggal yang berisi nilai jenis itu, Anda bisa sangat dekat dengan keamanan sintaksis / perilaku / tipe yang Anda inginkan. yaitu:

class Fruit
{
public:
  enum Value : uint8_t
  {
    Apple,
    Pear,
    Banana,
    Strawberry
  };

  Fruit() = default;
  constexpr Fruit(Value aFruit) : value(aFruit) { }

#if Enable switch(fruit) use case:
  operator Value() const { return value; }  // Allow switch and comparisons.
                                            // note: Putting constexpr here causes
                                            // clang to stop warning on incomplete
                                            // case handling.
  explicit operator bool() = delete;        // Prevent usage: if(fruit)
#else
  constexpr bool operator==(Fruit a) const { return value == a.value; }
  constexpr bool operator!=(Fruit a) const { return value != a.value; }
#endif

  constexpr bool IsYellow() const { return value == Banana; }

private:
  Value value;
};

Sekarang Anda dapat menulis:

Fruit f = Fruit::Strawberry;
f.IsYellow();

Dan kompiler akan mencegah hal-hal seperti:

Fruit f = 1;  // Compile time error.

Anda dapat dengan mudah menambahkan metode sedemikian rupa sehingga:

Fruit f("Apple");

dan

f.ToString();

dapat didukung.

jtlim
sumber
1
Tidakkah seharusnya juga IsYellow (), operator ==,! = Ditandai sebagai constexpr?
Jarek C
Saya mendapatkan "error: operator biner yang hilang sebelum token" switch ""
Pedro77
18

Berkonsentrasi pada deskripsi pertanyaan dan bukan judul jawaban yang mungkin

struct LowLevelMouseEvent {
    enum Enum {
        mouse_event_uninitialized = -2000000000, // generate crash if try to use it uninitialized.
        mouse_event_unknown = 0,
        mouse_event_unimplemented,
        mouse_event_unnecessary,
        mouse_event_move,
        mouse_event_left_down,
        mouse_event_left_up,
        mouse_event_right_down,
        mouse_event_right_up,
        mouse_event_middle_down,
        mouse_event_middle_up,
        mouse_event_wheel
    };
    static const char* ToStr (const type::LowLevelMouseEvent::Enum& event)
    {
        switch (event) {
            case mouse_event_unknown:         return "unknown";
            case mouse_event_unimplemented:   return "unimplemented";
            case mouse_event_unnecessary:     return "unnecessary";
            case mouse_event_move:            return "move";
            case mouse_event_left_down:       return "left down";
            case mouse_event_left_up:         return "left up";
            case mouse_event_right_down:      return "right down";
            case mouse_event_right_up:        return "right up";
            case mouse_event_middle_down:     return "middle down";
            case mouse_event_middle_up:       return "middle up";
            case mouse_event_wheel:           return "wheel";
            default:
                Assert (false);
                break;
        }
        return "";
    }
};
Márkus Attila
sumber
4

Seperti disebutkan dalam jawaban lain , tidak. Bahkan enum classbukan kelas.


Biasanya kebutuhan untuk memiliki metode untuk enumhasil dari alasan itu bukan enum biasa (hanya incrementing), tetapi jenis definisi nilai bitwise untuk disembunyikan atau memerlukan operasi bit-aritmatika lainnya:

enum class Flags : unsigned char {
    Flag1 = 0x01 , // Bit #0
    Flag2 = 0x02 , // Bit #1
    Flag3 = 0x04 , // Bit #3
    // aso ...
}

// Sets both lower bits
unsigned char flags = (unsigned char)(Flags::Flag1 | Flags::Flag2);

// Set Flag3
flags |= Flags::Flag3;

// Reset Flag2
flags &= ~Flags::Flag2;

Jelas orang berpikir untuk merangkum operasi yang diperlukan untuk mengatur ulang / menetapkan bit tunggal / kelompok, dengan nilai bit mask misalnya atau bahkan operasi berbasis indeks bit akan berguna untuk memanipulasi set seperti 'flags'.

Itu struct/ class Spesifikasi hanya mendukung pelingkupan yang lebih baik dari nilai enum untuk akses. Tidak lebih, tidak kurang!

Cara untuk keluar dari batasan Anda tidak dapat mendeklarasikan metode untuk enum (kelas) adalah, baik menggunakan std::bitset(kelas pembungkus), atau bitfieldunion .

unions, dan serikat bitfield semacam itu dapat memiliki metode (lihat di sini untuk pembatasan!).

Saya punya sampel, bagaimana mengkonversi nilai bit mask (seperti yang ditunjukkan di atas) ke indeks bit yang sesuai, yang dapat digunakan di std::bitsetsini: BitIndexConverter.hpp
Saya telah menemukan ini cukup berguna untuk meningkatkan keterbacaan dari beberapa keputusan berdasarkan 'flag' algoritma.

πάντα ῥεῖ
sumber
36
Ada lebih banyak kasus penggunaan yang menjamin metode pada enum classe, misalnya toString () dan fromString (). Setiap (bahkan tidak begitu) bahasa utama modern memiliki ini (misalnya C #, Java, Swift), hanya saja tidak C ++.
Mike Lischke
1
Mari kita berharap untuk sintaks panggilan terpadu waktu berikutnya ... open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4165.pdf
sdgfsdh
4

Ada kemampuan yang cukup kompatibel (§) untuk membuat ulang enum ke dalam kelas tanpa harus menulis ulang kode Anda, yang berarti bahwa secara efektif Anda dapat melakukan apa yang Anda minta tanpa terlalu banyak mengedit.

(§) seperti yang ditunjukkan ElementW dalam komentar, kode dependen type_traits tidak akan berfungsi, jadi misalnya seseorang tidak dapat menggunakan otomatis, dll. Mungkin ada beberapa cara untuk menangani hal-hal seperti itu, tetapi pada akhirnya seseorang mengubah enum menjadi sebuah kelas, dan selalu merupakan kesalahan untuk menumbangkan C ++

yang enum structdan enum classspesifikasi tentang scoping sehingga bukan bagian dari ini.

Enum orisinal Anda adalah misalnya 'hewan peliharaan' (ini hanya sebagai contoh!).

enum pet { 
    fish, cat, dog, bird, rabbit, other 
};

(1) Anda memodifikasinya menjadi misalnya petEnum (untuk menyembunyikannya dari kode yang ada)

enum petEnum { 
    fish, cat, dog, bird, rabbit, other 
};

(2) Anda menambahkan deklarasi kelas baru di bawahnya (dinamai dengan enum asli)

class pet {
    private:
        petEnum value;
        pet() {}

    public:
        pet(const petEnum& v) : value{v} {} //not explicit here.
        operator petEnum() const { return value; }
        pet& operator=(petEnum v) { value = v; return *this;}
        bool operator==(const petEnum v) const { return value == v; }
        bool operator!=(const petEnum v) const { return value != v; }
 //     operator std::string() const;

};

(3) Sekarang Anda dapat menambahkan metode kelas apa pun yang Anda suka ke kelas hewan peliharaan Anda. misalnya. operator string

    pet::operator std::string() const {
        switch (value) {
            case fish: return "fish";
            case cat:  return "cat";
            case dog:  return "dog";
            case bird: return "bird";
            case rabbit: return "rabbit";
            case other: return "Wow. How exotic of you!";
        }
    }

Sekarang Anda dapat menggunakan mis std :: cout ...

int main() {
    pet myPet = rabbit;
    if(myPet != fish) {
        cout << "No splashing! ";
    }
    std::cout << "I have a " << std::string(myPet) << std::endl;
    return 0;
}
Konchog
sumber
1
Ini tidak sepenuhnya kompatibel: jika Anda menggunakan nilai-nilai enum dengan segala jenis pengurang jenis di mana ia diharapkan untuk mendapatkan nama petketik / contoh, apakah itu templat auto,, atau decltype, ini rusak, karena Anda mendapatkan petEnumsebaliknya.
ElementW
0

Ini mungkin tidak memenuhi semua kebutuhan Anda, tetapi dengan operator non-anggota Anda masih bisa bersenang-senang. Sebagai contoh:

#include <iostream>

enum class security_level
{
    none, low, medium, high
};

static bool operator!(security_level s) { return s == security_level::none; }

static security_level& operator++(security_level& s)
{
    switch(s)
    {
        case security_level::none: s = security_level::low; break;
        case security_level::low: s = security_level::medium; break;
        case security_level::medium: s = security_level::high; break;
        case security_level::high: break;
    }
    return s;
}

static std::ostream & operator<<(std::ostream &o, security_level s)
{
    switch(s)
    {
        case security_level::none: return o << "none";
        case security_level::low: return o << "low";
        case security_level::medium: return o << "medium";
        case security_level::high: return o << "high";
    }
}

Ini memungkinkan kode seperti

security_level l = security_level::none;   
if(!!l) { std::cout << "has a security level: " << l << std::endl; } // not reached
++++l;
if(!!l) { std::cout << "has a security level: " << l << std::endl; } // reached: "medium"
Johannes
sumber