क्या C ++ enum वर्ग के तरीके हो सकते हैं?


145

मेरे पास दो मूल्यों के साथ एक एनम वर्ग है, और मैं एक विधि बनाना चाहता हूं जो एक मूल्य प्राप्त करता है और दूसरे को वापस करता है। मैं टाइप सेफ्टी भी बनाए रखना चाहता हूं (इसीलिए मैं एनम की जगह एनम क्लास का इस्तेमाल करता हूं)।

http://www.cplusplus.com/doc/tutorial/other_data_types/ तरीकों के बारे में कुछ भी उल्लेख नहीं करता है हालांकि, मैं इस धारणा के तहत था कि किसी भी प्रकार के वर्ग में विधियां हो सकती हैं।


4
नहीं ये नहीं हो सकता। देखें यहाँ
juanchopanza

@octavian मेरे उत्तर पर ध्यान दें और कृपया अपने उपयोग के मामलों के बारे में पुनर्विचार करें!
atν

@ πάν enαῥεῖ आप बिलकुल सही कह रहे हैं, मैंने enum पढ़ा है, लेकिन सोचा संघ ने टिप्पणी को मार दिया।
यूजेन कांस्टेंटिन दिनका

@octavian क्या आप भी किसी विशेष मामले का उपयोग करने के लिए कह रहे हैं, या क्या आप चाहते हैं कि c ++ 11 पर मानक प्रतिबंध enum class/structकी पुष्टि हो?
atν 1:α

मेरे मन में एक प्रयोग था ... और यह मूल मुद्दा था
ओक्टेवियन

जवाबों:


118

नहीं, वे नहीं कर सकते।

मैं समझ सकता हूं कि enum classC ++ 11 में दृढ़ता से टाइप किए गए एनम के लिए भाग का मतलब यह लग सकता है कि आपके enumपास classभी लक्षण हैं, लेकिन यह ऐसा नहीं है। मेरा शिक्षित अनुमान यह है कि कीवर्ड की पसंद उस पैटर्न से प्रेरित थी जिसे हमने सी + + 11 से पहले इस्तेमाल किया था ताकि वे स्कॉप्ड एनम प्राप्त कर सकें।

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

हालाँकि, यह सिर्फ वाक्यविन्यास है। फिर, enum classएक नहीं है class


88
## C ++ पर मुझे बताया गया कि "c ++ का उद्देश्य भ्रमित करना और यथासंभव विशेषज्ञ अनुकूल होना है" । जाहिर है कि यह एक मजाक है, लेकिन आप इस विचार को प्राप्त करते हैं :)
स्टेफानो सैनफिलिपो

4
unionनहीं है जो जॉन डो एक वर्ग पर भी विचार करेगा । फिर भी उनके सदस्य कार्य हो सकते हैं। और सदस्य कार्यों के लिए कक्षाएं वास्तव में अनिवार्य नहीं हैं। जैसे , valueया जैसे thisकुछ enum Size { Huge, Mega, Apocalypse; bool operator<(X rhs) const { return *this < rhs; }(यहां भी अनुमति दे रहा है ;) के रूप में एक डिजाइनर का उपयोग करना , यह अन्य कार्यों के रूप में बस उतना ही समझ सकता है।
सेबस्टियन मच

85

हालांकि यह जवाब कि "आप नहीं कर सकते" तकनीकी रूप से सही है, मेरा मानना ​​है कि आप उस व्यवहार को प्राप्त करने में सक्षम हो सकते हैं जिसे आप निम्नलिखित विचार का उपयोग कर रहे हैं:

मुझे लगता है कि आप कुछ लिखना चाहते हैं:

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

और आप उम्मीद कर रहे थे कि कोड कुछ इस तरह दिखता है:

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

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

...

लेकिन निश्चित रूप से, यह काम नहीं करता है, क्योंकि enums में तरीके नहीं हो सकते हैं (और 'यह' उपरोक्त संदर्भ में कुछ भी मतलब नहीं है)

हालाँकि, यदि आप एक सामान्य वर्ग के विचार का उपयोग करते हैं जिसमें एक गैर-वर्ग एनुम और एक एकल सदस्य चर होता है जिसमें उस प्रकार का मान होता है, तो आप सिंटैक्स / व्यवहार / प्रकार की सुरक्षा जो आप चाहते हैं, के बेहद करीब पा सकते हैं। अर्थात:

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

अब आप लिख सकते हैं:

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

और कंपाइलर चीजों को रोकेगा:

Fruit f = 1;  // Compile time error.

आप आसानी से इस तरह के तरीकों को जोड़ सकते हैं:

Fruit f("Apple");

तथा

f.ToString();

का समर्थन किया जा सकता है।


1
IsYellow (), ऑपरेटर ==; = भी नहीं होना चाहिए?
जारेक सी

मैं "त्रुटि: बाइनरी ऑपरेटर को टोकन" स्विच "से पहले गायब कर रहा हूं
पेड्रो77

18

शीर्षक के बजाय प्रश्न के विवरण पर ध्यान केंद्रित करना एक संभावित उत्तर है

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

जैसा कि अन्य उत्तर में बताया गया है , नहीं। यहां तक ​​कि enum classएक वर्ग भी नहीं है।


आमतौर पर परिणाम के तरीकों की आवश्यकता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;

जाहिर है कि कोई भी बिट्स के समूह / बिट्स को फिर से सेट / सेट करने के लिए आवश्यक ऑपरेशनों को इनकैप्सुलेट करने के बारे में सोचता है, जैसे बिट मास्क वैल्यू या बिट इंडेक्स संचालित ऑपरेशंस ऐसे 'फ्लैग' के सेट के हेरफेर के लिए उपयोगी होंगे।

struct/ class विनिर्देश उपयोग के लिए बस एनम मूल्यों के बेहतर स्कूपिंग का समर्थन करता है। न आधिक न कम!

प्रतिबंध से बाहर निकलने के तरीके आप एनम (वर्गों) के लिए तरीकों की घोषणा नहीं कर सकते हैं, या तो एक std::bitset(आवरण वर्ग), या एक बिटफील्डunion का उपयोग करने के लिए हैं ।

unions, और ऐसे बिटफील्ड यूनियनों में विधियाँ हो सकती हैं ( प्रतिबंधों के लिए यहाँ देखें !)।

मेरे पास एक नमूना है, बिट मास्क मूल्यों को कैसे परिवर्तित किया जाए (जैसा कि ऊपर दिखाया गया है) उनके संबंधित बिट सूचकांकों के लिए, जिसका उपयोग std::bitsetयहां किया जा सकता है: BitIndexConverter.hpp
मैंने इसे कुछ 'ध्वज' की पठनीयता बढ़ाने के लिए बहुत उपयोगी पाया है। एल्गोरिदम।


36
अधिक उपयोग के मामले हैं जो एनम क्लैस पर वारंट के तरीकों, उदाहरण के लिए स्ट्रींग () और डेस्ट्रिंग () से हैं। हर (यहां तक ​​कि नहीं) आधुनिक प्रमुख भाषा में यह है (जैसे सी #, जावा, स्विफ्ट), बस सी ++ नहीं।
माइक लिस्चके

1
आइए अगली बार एकीकृत कॉल सिंटैक्स की उम्मीद करें ... खुले-std.org/jtc1/sc22/wg21/docs/papers/2014/n4165.pdf
sdgfsdh

4

अपने कोड को फिर से लिखने के बिना एक वर्ग में एक एनम को फिर से भरने के लिए एक बहुत संगत क्षमता ( ability ) है, जिसका अर्थ है कि प्रभावी रूप से आप वह कर सकते हैं जो आप बहुत अधिक संपादन के बिना करने के लिए कह रहे थे।

(,) जैसा कि एलिमेंट डब्ल्यू एक टिप्पणी में बताते हैं , टाइप_ट्रेट्स निर्भर कोड काम नहीं करेगा, इसलिए जैसे कोई ऑटो का उपयोग नहीं कर सकता है, आदि ऐसे सामान को संभालने का कुछ तरीका हो सकता है, लेकिन अंत में एक एनम को एक वर्ग में परिवर्तित कर रहा है, आदि। और यह हमेशा C ++ को हटाने के लिए एक गलती है

enum structऔर enum classविनिर्देशों इसलिए इस का हिस्सा नहीं देखते हुए के बारे में कर रहे हैं।

आपकी मूल गणना उदा 'पालतू' है (यह केवल एक उदाहरण के रूप में है!)।

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

(1) आप इसे उदाहरण के लिए पेटीएम में संशोधित करते हैं (ताकि इसे अपने मौजूदा कोड से छिपा सकें)।

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

(2) आप इसके नीचे एक नई श्रेणी की घोषणा जोड़ते हैं (मूल एनम के साथ नाम)

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;

};

(३) अब आप अपने पालतू जानवरों की कक्षा को जो भी पसंद कर सकते हैं, जोड़ सकते हैं। जैसे। एक स्ट्रिंग ऑपरेटर

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

अब आप उदाहरणार्थ 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;
}

1
यह पूरी तरह से संगत नहीं है : यदि आप किसी भी प्रकार की कटौती के साथ petएनम मूल्यों का उपयोग करते हैं, जहां यह टाइपनेम / उदाहरण प्राप्त करने की अपेक्षा की जाती है , तो यह टेम्पलेट हो auto, या decltype, यह टूट जाता है, जैसा कि आप petEnumइसके बजाय प्राप्त करते हैं।
तत्ववचन

0

यह आपकी सभी जरूरतों को पूरा नहीं कर सकता है, लेकिन गैर-सदस्य ऑपरेटरों के साथ आप अभी भी बहुत मज़ा कर सकते हैं। उदाहरण के लिए:

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

यह कोड की तरह अनुमति देता है

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"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.