Bir C ++ enum sınıfının yöntemleri olabilir mi?


145

İki değer içeren bir numaralandırma sınıfı var ve bir değer alan ve diğerini döndüren bir yöntem oluşturmak istiyorum. Ayrıca tip güvenliğini korumak istiyorum (bu yüzden numaralandırmalar yerine numaralandırma sınıfını kullanıyorum).

http://www.cplusplus.com/doc/tutorial/other_data_types/ yöntemler hakkında hiçbir şeyden bahsetmez Ancak, herhangi bir sınıf türünün yöntemleri olabileceği izlenimi altındaydım.


4
Hayır bu olamaz. Buraya bakın .
juanchopanza

@octavian Cevabımı not et ve kullanım durumları hakkında tekrar düşün !
ῥεῖντα ῥεῖ

@ πάνταῥεῖ tamamen haklısın, enum okudum ama sendika düşündüm, yorumu öldürdüm.
Eugen Constantin Dinca

@octavian Hatta belirli bir kullanım örneği mi istiyorsunuz, yoksa sadece c ++ 11'deki standart kısıtlamalarının enum class/structonaylanmasını mı istiyorsunuz?
ῥεῖντα ῥεῖ

Aklımda bir kullanım vardı ... ve temel sorun
buydu

Yanıtlar:


118

Hayır, yapamazlar.

enum classC ++ 11'de güçlü bir şekilde yazılmış numaralandırmalar için parçanın enumda classözelliklerinizin olduğunu ima ediyor gibi görünebilir , ancak durum böyle değil. Eğitimli tahminim, anahtar kelimelerin seçiminin, kapsamlandırılmış numaralandırmalar almak için C ++ 11'den önce kullandığımız kalıptan ilham aldığını:

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

Ancak, bu sadece sözdizimidir. Yine, enum classbir değil class.


88
## C ++ 'da "c ++' ın mümkün olduğunca kafa karıştırıcı ve uzman dostu olmayı amaçladığı" söylendi . Açıkçası bu bir şaka, ama fikri anladın :)
Stefano Sanfilippo

4
A union, John Doe'nun da bir sınıfı düşüneceği şey değildir . Yine de üye işlevleri olabilir. Ve sınıflar üye işlevleri için gerçekten zorunlu değildir. Bir benzeri tanımlayıcılı kullanılması valueveya thisbenzeri bir şey enum Size { Huge, Mega, Apocalypse; bool operator<(X rhs) const { return *this < rhs; }(burada da izin ;), sadece işlevlerinin diğer formları gibi çok anlamda olarak yapabilirsiniz.
Sebastian Mach

85

"Yapamazsınız" cevabı teknik olarak doğru olsa da, aradığınız davranışı aşağıdaki fikri kullanarak elde edebileceğinizi düşünüyorum:

Şöyle bir şey yazmak istediğinizi hayal ediyorum:

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

Ve kodun şöyle görüneceğini umuyordunuz:

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

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

...

Tabii ki işe yaramaz, çünkü numaralandırmaların yöntemleri olamaz (ve 'bu' yukarıdaki bağlamda hiçbir şey ifade etmez)

Ancak, sınıf dışı bir enum içeren normal bir sınıf ve bu türde bir değer içeren tek bir üye değişkeni fikrini kullanırsanız, istediğiniz sözdizimi / davranış / tür güvenliğine son derece yaklaşabilirsiniz. yani:

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;
};

Şimdi yazabilirsiniz:

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

Ve derleyici aşağıdaki gibi şeyleri önleyecektir:

Fruit f = 1;  // Compile time error.

Kolayca aşağıdaki gibi yöntemler ekleyebilirsiniz:

Fruit f("Apple");

ve

f.ToString();

desteklenebilir.


1
Ayrıca IsYellow (), operatörü ==,! = Constexpr olarak işaretlenmemelidir?
Jarek C

"Hatası alıyorum:" switch "anahtarından önce ikili operatör eksik
Pedro77

18

Başlığın yerine sorunun açıklamasına yoğunlaşmak olası bir cevaptır.

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 "";
    }
};

4

Diğer cevapta belirtildiği gibi , hayır. enum classSınıf bile değil.


Genellikle, düzenli (sadece artan) bir numaralandırma değil, maskelenecek veya diğer bit-aritmetik işlemlere ihtiyaç duyan değerlerin bitsel olarak tanımlanmasının bir sonucuna yönelik yöntemlere sahip olma ihtiyacı :enum

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;

Açıkçası, tek bir bit grubunu yeniden bitirmek / ayarlamak için, örneğin bit maskesi değeri ve hatta bit indeksine dayalı işlemler ile gerekli işlemleri kapsüllemek düşünülmektedir.

struct/ class belirtimi erişim için enum değerlerinin daha iyi kapsamasını destekler. Ne fazla ne az!

Enum (sınıflar) için yöntem bildiremeyeceğiniz kısıtlamanın dışına çıkmanın yolları ya std::bitset(sarmalayıcı sınıfı) ya da bir bit alanı kullanmaktırunion .

unions ve bu tür bitfield sendikalarının yöntemleri olabilir ( kısıtlamalar için buraya bakın !).

Bit maskesi değerleri (yukarıda gösterildiği gibi) bir std::bitsetburada boyunca kullanılabilir karşılık gelen bit indeksleri dönüştürmek nasıl bir örnek var: BitIndexConverter.hpp
Bu bazı 'bayrak' decison tabanlı okunabilirliğini artırmak için oldukça yararlı buldum algoritmaları.


36
Enum classe üzerinde, toString () ve fromString () gibi yöntemleri gerektiren daha fazla kullanım durumu vardır. Her modern ana dilde (örneğin C #, Java, Swift) vardır, C ++ değil.
Mike Lischke

1
Bir dahaki sefere birleşik çağrı sözdizimi için umut edelim ... open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4165.pdf
sdgfsdh

4

Kodunuzu yeniden yazmanıza gerek kalmadan bir numaralandırmayı sınıfa yeniden sığdırmak için oldukça uyumlu bir yetenek (§) vardır, bu da çok fazla düzenleme yapmadan yapmak istediğinizi etkili bir şekilde yapabileceğiniz anlamına gelir .

(§) ElementW'nin bir yorumda belirttiği gibi, type_traits bağımlı kodu çalışmaz, bu yüzden biri otomatik kullanamaz, vb. ve C ++ 'ı bozmak her zaman bir hatadır

enum structve enum classteknik özellikleri bu nedenle bu değil parçasını kapsam hakkındadır.

Orijinal numaralandırma örneğin "evcil hayvan" dır (bu sadece örnek olarak verilmiştir!).

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

(1) Bunu örneğin petEnum olarak değiştirirsiniz (mevcut kodunuzdan gizlemek için).

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

(2) Altına yeni bir sınıf bildirimi eklersiniz (orijinal numaralandırma ile adlandırılır)

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) Artık evcil hayvan sınıfınıza istediğiniz sınıf yöntemlerini ekleyebilirsiniz. Örneğin. bir dize operatörü

    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!";
        }
    }

Şimdi örneğin std :: cout kullanabilirsiniz ...

int main() {
    pet myPet = rabbit;
    if(myPet != fish) {
        cout << "No splashing! ";
    }
    std::cout << "I have a " << std::string(myPet) << std::endl;
    return 0;
}

1
Öyle değil tam uyumlu: bunu bir alması beklenen tip kesinti her türlü enum değerleri kullanmak durumunda pettypename / örneğini, bu şablonları olmak, autoya decltypesen olsun, bu tatili petEnumyerine.
ElementW

0

Tüm ihtiyaçlarınızı karşılamayabilir, ancak üye olmayan operatörlerle hala çok eğlenebilirsiniz. Örneğin:

#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";
    }
}

Bu kod gibi

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"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.