सादे एनम पर एनम वर्ग को क्यों पसंद किया जाता है?


429

मैंने कुछ लोगों को उनकी प्रकार की सुरक्षा के कारण C ++ में enum classes का उपयोग करने की सलाह देते हुए सुना ।

लेकिन असल में उसका क्या अर्थ है?


57
जब कोई दावा करता है कि कुछ प्रोग्रामिंग निर्माण "बुराई" है, तो वे आपको अपने लिए सोचने से हतोत्साहित करने की कोशिश कर रहे हैं।
पीट बेकर

3
@ नाइकोलोलस: यह अक्सर पूछे जाने वाले प्रश्न का जवाब देने के लिए एक पुनरावृत्ति संबंधी प्रश्न है (क्या यह वास्तव में अक्सर पूछे जाने वाली एक अलग कहानी है)।
डेविड रॉड्रिग्ज़ - drieaseas

@ डेविड, एक चर्चा है कि क्या यह एक एफएक्यू होना चाहिए या नहीं जो यहां शुरू होता है । इनपुट का स्वागत
sbi

17
@PeteBecker कभी-कभी वे केवल आपको खुद से बचाने की कोशिश कर रहे हैं ।
piccy

geeksforgeeks.org/… यह भी enumबनाम समझने के लिए एक अच्छी जगह है enum class
mr_azad

जवाबों:


472

C ++ के दो प्रकार हैं enum:

  1. enum classes
  2. सादा enums

यहाँ कुछ उदाहरण दिए गए हैं कि उन्हें कैसे घोषित किया जाए:

 enum class Color { red, green, blue }; // enum class
 enum Animal { dog, cat, bird, human }; // plain enum 

दो में क्या अंतर है?

  • enum classes - एन्यूमरेटर नाम एनम के लिए स्थानीय हैं और उनके मान अन्य प्रकार (जैसे या किसी अन्य ) में रूपांतरित नहीं होते हैंenumint

  • प्लेन enums - जहां एन्यूमरेटर नाम एक ही दायरे में हैं क्योंकि एनम और उनके मानों को संक्षेप में पूर्णांकों और अन्य प्रकारों में बदल दिया जाता है

उदाहरण:

enum Color { red, green, blue };                    // plain enum 
enum Card { red_card, green_card, yellow_card };    // another plain enum 
enum class Animal { dog, deer, cat, bird, human };  // enum class
enum class Mammal { kangaroo, deer, human };        // another enum class

void fun() {

    // examples of bad use of plain enums:
    Color color = Color::red;
    Card card = Card::green_card;

    int num = color;    // no problem

    if (color == Card::red_card) // no problem (bad)
        cout << "bad" << endl;

    if (card == Color::green)   // no problem (bad)
        cout << "bad" << endl;

    // examples of good use of enum classes (safe)
    Animal a = Animal::deer;
    Mammal m = Mammal::deer;

    int num2 = a;   // error
    if (m == a)         // error (good)
        cout << "bad" << endl;

    if (a == Mammal::deer) // error (good)
        cout << "bad" << endl;

}

निष्कर्ष:

enum classएस को प्राथमिकता दी जानी चाहिए क्योंकि वे कम आश्चर्य का कारण बनते हैं जो संभवतः कीड़े के लिए नेतृत्व कर सकते हैं।


7
अच्छा उदाहरण ... क्या एनाम संस्करण के नामस्थान पदोन्नति के साथ वर्ग संस्करण की सुरक्षा को संयोजित करने का एक तरीका है? यही है, अगर मेरे पास Aराज्य के साथ एक वर्ग है, और मैं enum class State { online, offline };कक्षा के एक बच्चे के रूप में बनाता Aहूं, तो मैं इसके बजाय state == onlineचेक करना चाहूंगा ... क्या यह संभव है? Astate == State::online
निशान

31
नहीं। नेमस्पेस प्रमोशन एक बैड थिंग ™ है और enum classइसे खत्म करने का आधा औचित्य था।
पिल्ला

10
C ++ 11 में, आप स्पष्ट रूप से टाइप किए गए एनम का उपयोग कर सकते हैं, जैसे कि एनम पशु: अहस्ताक्षरित int {कुत्ता, हिरण, बिल्ली, पक्षी}
Blasius Secundus

3
@ कैट प्लस प्लस मैं समझता हूं कि @ ओलेक्सी कहते हैं कि यह खराब है। मेरा सवाल यह नहीं था कि ओलेक्सी ने सोचा कि यह बुरा है। मेरा प्रश्न यह बताने का अनुरोध था कि इसके बारे में क्या बुरा है। विशेष रूप से, ओलेक्सी, उदाहरण के लिए, बुरा क्यों मानता है Color color = Color::red
chux -

9
@ काॅट प्लस प्लस तो उदाहरण का खराब होनाif (color == Card::red_card) लाइन तक नहीं होता है, टिप्पणी की तुलना में 4 लाइनों के बाद (जो मुझे अब ब्लॉक के पहले छमाही पर लागू होता है।) ब्लॉक की 2 लाइनें खराब उदाहरण देती हैं। पहली 3 लाइनें कोई समस्या नहीं हैं। "पूरे ब्लॉक क्यों सादे enums बुरा है" मुझे फेंक दिया क्योंकि मुझे लगा कि आप का मतलब है कि कुछ भी उन लोगों के साथ गलत था। मैं अब देख रहा हूं, यह सिर्फ एक सेट-अप है। किसी भी मामले में, प्रतिक्रिया के लिए धन्यवाद।
chux -

248

से Bjarne Stroustrup की सी ++ 11 अकसर किये गए सवाल :

enum classतों ( "नया enums", "मजबूत enums") पता परंपरागत सी ++ enumerations के साथ तीन समस्याओं:

  • पारंपरिक रूप से गुप्त रूप से इंट में परिवर्तित हो जाते हैं, जिससे त्रुटियां तब होती हैं जब कोई एक पूर्णांक के रूप में कार्य करना नहीं चाहता है।
  • पारंपरिक एनम अपने संयुक्ताक्षरों को आसपास के दायरे में निर्यात करते हैं, जिससे नाम की गड़बड़ी होती है।
  • अंतर्निहित प्रकार को enumनिर्दिष्ट नहीं किया जा सकता है, जिससे भ्रम, संगतता समस्याएं पैदा होती हैं, और आगे की घोषणा को असंभव बना देता है।

नई दुश्मनी "एनम क्लास" है क्योंकि वे पारंपरिक एनुमरेशंस (नाम मूल्यों) के पहलुओं को वर्गों के पहलुओं (स्कोप्ड सदस्यों और रूपांतरणों की अनुपस्थिति) के साथ जोड़ती हैं।

इसलिए, जैसा कि अन्य उपयोगकर्ताओं द्वारा कहा गया है, "मजबूत एनम" कोड को सुरक्षित बना देगा।

"क्लासिक" का अंतर्निहित प्रकार enumएक पूर्णांक प्रकार होगा जो सभी के मूल्यों को फिट करने के लिए पर्याप्त है enum; यह आमतौर पर एक है int। इसके अलावा प्रत्येक प्रगणित प्रकार संगत होगाchar एक हस्ताक्षरित / अहस्ताक्षरित पूर्णांक प्रकार ।

यह एक विस्तृत विवरण है कि एक enumअंतर्निहित प्रकार क्या होना चाहिए, इसलिए प्रत्येक कंपाइलर क्लासिक के अंतर्निहित प्रकार के बारे में अपने दम पर निर्णय लेगाenum और कभी-कभी परिणाम आश्चर्यजनक हो सकता है।

उदाहरण के लिए, मैंने कई बार इस तरह का एक कोड देखा है:

enum E_MY_FAVOURITE_FRUITS
{
    E_APPLE      = 0x01,
    E_WATERMELON = 0x02,
    E_COCONUT    = 0x04,
    E_STRAWBERRY = 0x08,
    E_CHERRY     = 0x10,
    E_PINEAPPLE  = 0x20,
    E_BANANA     = 0x40,
    E_MANGO      = 0x80,
    E_MY_FAVOURITE_FRUITS_FORCE8 = 0xFF // 'Force' 8bits, how can you tell?
};

ऊपर दिए गए कोड में, कुछ भोले-भाले कोडर सोच रहे हैं कि कंपाइलर E_MY_FAVOURITE_FRUITSमानों को एक अहस्ताक्षरित 8 बिट प्रकार में संग्रहीत करेगा ... लेकिन इसके बारे में कोई वारंटी नहीं है: कंपाइलर उन सभी में से किसी एक प्रकार का चयन unsigned charया intया कर सकता है short, जो किसी भी प्रकार के बड़े हैं। में देखे गए मूल्य enum। फ़ील्ड जोड़ना E_MY_FAVOURITE_FRUITS_FORCE8एक बोझ है और यह कंपाइलर को अंतर्निहित प्रकार के बारे में किसी भी तरह का चुनाव करने के लिए मजबूर नहीं करता है enum

यदि कोड का कोई टुकड़ा है जो प्रकार के आकार और / या पर भरोसा करता है जो कि E_MY_FAVOURITE_FRUITSकुछ चौड़ाई का होगा (जैसे: क्रमांकन दिनचर्या) यह कोड संकलक के विचारों के आधार पर कुछ अजीब तरीकों से व्यवहार कर सकता है।

और मामले को बदतर बनाने के लिए, अगर कुछ वर्कमेट लापरवाही से हमारे लिए एक नया मूल्य जोड़ता है enum:

    E_DEVIL_FRUIT  = 0x100, // New fruit, with value greater than 8bits

संकलक इसके बारे में शिकायत नहीं करता है! यह बस के सभी मूल्यों को फिट करने के प्रकार का enumअनुमान लगाता है (यह मानते हुए कि संकलक संभव छोटे प्रकार का उपयोग कर रहे हैं, जो एक धारणा है कि हम नहीं कर सकते हैं)। इस सरल और लापरवाह इसके अलावाenum संबंधित कोड को तोड़ सकता है।

चूंकि C ++ 11 के लिए अंतर्निहित प्रकार enumऔर enum class(धन्यवाद rdb ) को निर्दिष्ट करना संभव है, इसलिए इस मुद्दे को बड़े करीने से संबोधित किया गया है:

enum class E_MY_FAVOURITE_FRUITS : unsigned char
{
    E_APPLE        = 0x01,
    E_WATERMELON   = 0x02,
    E_COCONUT      = 0x04,
    E_STRAWBERRY   = 0x08,
    E_CHERRY       = 0x10,
    E_PINEAPPLE    = 0x20,
    E_BANANA       = 0x40,
    E_MANGO        = 0x80,
    E_DEVIL_FRUIT  = 0x100, // Warning!: constant value truncated
};

अंतर्निहित प्रकार को निर्दिष्ट करना यदि किसी क्षेत्र में इस प्रकार की सीमा से बाहर की अभिव्यक्ति है तो संकलक अंतर्निहित प्रकार को बदलने के बजाय शिकायत करेगा।

मुझे लगता है कि यह एक अच्छा सुरक्षा सुधार है।

तो एनम वर्ग को सादे एनम पर क्यों पसंद किया जाता है? , अगर हम स्कोप्ड ( enum class) और अनकैप्ड ( enum) एनम के लिए अंतर्निहित प्रकार का चयन कर सकते हैं तो और क्या enum classबेहतर विकल्प है ?:

  • वे अंतर्निहित रूप से परिवर्तित नहीं होते हैं int
  • वे आसपास के नाम स्थान को प्रदूषित नहीं करते हैं।
  • उन्हें आगे घोषित किया जा सकता है।

1
मुझे लगता है कि हम नियमित रूप से enums के लिए enum बेस टाइप को प्रतिबंधित कर सकते हैं, इसलिए जब तक हमारे पास C ++ 11 है
सागर पधिये

11
क्षमा करें, लेकिन यह उत्तर गलत है। "enum class" का प्रकार निर्दिष्ट करने की क्षमता से कोई लेना-देना नहीं है। यह एक स्वतंत्र विशेषता है जो नियमित रूप से एनम और एनम वर्गों के लिए दोनों मौजूद है।
rdb

14
यह सौदा है: * En ++ कक्षाएं C ++ 11 में एक नई सुविधा हैं। * टाइप किए गए एनम C ++ 11 में एक नई सुविधा है। C ++ 11 में ये दो अलग-अलग असंबंधित नई सुविधाएँ हैं। आप दोनों का उपयोग कर सकते हैं, या आप या तो उपयोग कर सकते हैं, या न ही।
rdb

2
मुझे लगता है कि एलेक्स एलन ने इस ब्लॉग में अभी तक [ cprogramming.com/c++11/] पर सबसे सरल विवरण दिया है । पारंपरिक enum पूर्णांक मान के स्थान पर उनके नाम और पूर्वप्रक्रमक #defines है, जो एक अच्छी बात थी का उपयोग कर से बचने के लिए अच्छा था - यह स्पष्टता गयी। enum class एन्यूमरेटर के एक संख्यात्मक मान की अवधारणा को हटाता है, और गुंजाइश और मजबूत टाइपिंग का परिचय देता है जो बढ़ता है (अच्छी तरह से, :-) प्रोग्राम शुद्धता बढ़ा सकता है । यह आपको ऑब्जेक्ट ओरिएंटेड सोचने के करीब एक कदम बढ़ाता है।
जॉन स्पेंसर

2
एक तरफ के रूप में, यह हमेशा मनोरंजक है जब आप कोड की समीक्षा कर रहे हैं और अचानक एक टुकड़ा होता है।
जस्टिन टाइम - मोनिका

47

सामान्य एनमों पर एनम वर्ग का उपयोग करने का मूल लाभ यह है कि आपके पास 2 अलग-अलग एनमों के लिए समान एनम चर हो सकते हैं और फिर भी उन्हें हल कर सकते हैं (जिसका उल्लेख प्रकार सुरक्षित है ओपी द्वारा )

उदाहरण के लिए:

enum class Color1 { red, green, blue };    //this will compile
enum class Color2 { red, green, blue };

enum Color1 { red, green, blue };    //this will not compile 
enum Color2 { red, green, blue };

मूल एनम के लिए, कंपाइलर यह भेद नहीं redकर पाएगा कि वह कथन के अनुसार टाइप Color1कर रहा है या नहीं Color2

enum Color1 { red, green, blue };   
enum Color2 { red, green, blue };
int x = red;    //Compile time error(which red are you refering to??)

1
@ ओलेक्सी ओह मैंने आपका प्रश्न ठीक से नहीं पढ़ा। विचार उन लोगों के लिए एक ऐड-ऑन के रूप में है जो नहीं जानते थे।
सकाम

यह ठीक है! मैं लगभग इस बारे में भूल गया
ओलेक्सी

बेशक, आप enum { COLOR1_RED, COLOR1_GREE, COLOR1_BLUE }नाम स्थान के मुद्दों को आसानी से लिखेंगे । नाम स्थान तर्क तीन में से एक है जिसका उल्लेख यहां किया गया है जो मैं बिल्कुल नहीं खरीदता हूं।
जो सो

2
@ तो यह समाधान एक अनावश्यक समाधान है। Enum: enum Color1 { COLOR1_RED, COLOR1_GREEN, COLOR1_BLUE }Enum वर्ग के लिए तुलनीय है enum class Color1 { RED, GREEN, BLUE }:। एक्सेस करना समान है: COLOR1_REDबनाम Color1::RED, लेकिन एनम संस्करण के लिए आपको प्रत्येक मूल्य में "COLOR1" टाइप करना होगा, जो टाइपोस के लिए अधिक जगह देता है, जो एनम वर्ग के नाम स्थान के व्यवहार से बचता है।
cdgraham

2
कृपया रचनात्मक आलोचना का उपयोग करें । जब मैं टाइपो के लिए अधिक जगह कहता हूं, मेरा मतलब है कि जब आप मूल रूप से उन मूल्यों को परिभाषित करते हैं enum Color1, जो एक संकलक नहीं पकड़ सकता है क्योंकि यह संभवतः अभी भी एक 'मान्य' नाम होगा। यदि मैं लिखता हूं RED, GREENऔर एक एनम वर्ग का उपयोग कर रहा हूं , तो इससे हल नहीं हो सकता enum Bananaक्योंकि इसके लिए आपको Color1::REDमूल्य (नाम स्थान तर्क) तक पहुंचने के लिए निर्दिष्ट करना होगा । उपयोग करने के लिए अभी भी अच्छे समय हैं enum, लेकिन कैन के नाम स्थान का व्यवहार enum classअक्सर बहुत फायदेमंद हो सकता है।
cdgraham

20

एन्यूमरेशन का उपयोग पूर्णांक मानों के एक समूह का प्रतिनिधित्व करने के लिए किया जाता है।

यह निर्दिष्ट classकरने के बाद enumकि एन्यूमरेशन दृढ़ता से टाइप किया गया है और उसके एन्यूमरेटर्स को स्कोप किया गया है। इस तरह enumकक्षाएं स्थिरांक के आकस्मिक दुरुपयोग को रोकती हैं।

उदाहरण के लिए:

enum class Animal{Dog, Cat, Tiger};
enum class Pets{Dog, Parrot};

यहां हम पशु और पालतू जानवरों के मूल्यों को नहीं मिला सकते हैं।

Animal a = Dog;       // Error: which DOG?    
Animal a = Pets::Dog  // Pets::Dog is not an Animal

7

C ++ 11 FAQ में नीचे दिए गए बिंदुओं का उल्लेख है:

पारंपरिक रूप से गुप्त रूप से इंट में परिवर्तित हो जाते हैं, जिससे त्रुटियां तब होती हैं जब कोई एक पूर्णांक के रूप में कार्य करना नहीं चाहता है।

enum color
{
    Red,
    Green,
    Yellow
};

enum class NewColor
{
    Red_1,
    Green_1,
    Yellow_1
};

int main()
{
    //! Implicit conversion is possible
    int i = Red;

    //! Need enum class name followed by access specifier. Ex: NewColor::Red_1
    int j = Red_1; // error C2065: 'Red_1': undeclared identifier

    //! Implicit converison is not possible. Solution Ex: int k = (int)NewColor::Red_1;
    int k = NewColor::Red_1; // error C2440: 'initializing': cannot convert from 'NewColor' to 'int'

    return 0;
}

पारंपरिक एनम अपने संयुक्ताक्षरों को आसपास के दायरे में निर्यात करते हैं, जिससे नाम की गड़बड़ी होती है।

// Header.h

enum vehicle
{
    Car,
    Bus,
    Bike,
    Autorickshow
};

enum FourWheeler
{
    Car,        // error C2365: 'Car': redefinition; previous definition was 'enumerator'
    SmallBus
};

enum class Editor
{
    vim,
    eclipes,
    VisualStudio
};

enum class CppEditor
{
    eclipes,       // No error of redefinitions
    VisualStudio,  // No error of redefinitions
    QtCreator
};

एक प्रकार का एनम निर्दिष्ट नहीं किया जा सकता है, जिससे भ्रम, अनुकूलता की समस्याएं होती हैं, और आगे की घोषणा को असंभव बना देता है।

// Header1.h
#include <iostream>

using namespace std;

enum class Port : unsigned char; // Forward declare

class MyClass
{
public:
    void PrintPort(enum class Port p);
};

void MyClass::PrintPort(enum class Port p)
{
    cout << (int)p << endl;
}

// Header.h
enum class Port : unsigned char // Declare enum type explicitly
{
    PORT_1 = 0x01,
    PORT_2 = 0x02,
    PORT_3 = 0x04
};

// Source.cpp
#include "Header1.h"
#include "Header.h"

using namespace std;
int main()
{
    MyClass m;
    m.PrintPort(Port::PORT_1);

    return 0;
}

C ++ 11 "गैर-श्रेणी" के साथ ही टाइप करने की अनुमति देता है । नाम स्थान के प्रदूषण के मुद्दे, आदि, अभी भी मौजूद हैं। प्रासंगिक जवाबों पर एक नज़र डालें जो इस एक के बहुत पहले से मौजूद थे ..
user2864740

7
  1. संक्षेप में int में परिवर्तित न करें
  2. किस प्रकार का चयन कर सकते हैं
  3. प्रदूषण से बचने के लिए ENUM नाम स्थान
  4. सामान्य वर्ग की तुलना में, आगे घोषित किया जा सकता है, लेकिन इसके तरीके नहीं हैं

2

यह ध्यान देने योग्य है, इन अन्य उत्तरों के शीर्ष पर, कि C ++ 20 में से एक समस्या enum classहै: वाचालता। एक काल्पनिक कल्पना enum class, Color

void foo(Color c)
  switch (c) {
    case Color::Red: ...;
    case Color::Green: ...;
    case Color::Blue: ...;
    // etc
  }
}

यह सादे enumभिन्नता की तुलना में क्रिया है , जहां नाम वैश्विक दायरे में हैं और इसलिए इसके साथ उपसर्ग करने की आवश्यकता नहीं है Color::

हालाँकि, C ++ 20 में हम using enumसमस्या को हल करने के लिए, enum में सभी नामों को वर्तमान दायरे में लाने के लिए उपयोग कर सकते हैं ।

void foo(Color c)
  using enum Color;
  switch (c) {
    case Red: ...;
    case Green: ...;
    case Blue: ...;
    // etc
  }
}

तो अब, उपयोग नहीं करने का कोई कारण नहीं है enum class


1

क्योंकि, जैसा कि अन्य उत्तरों में कहा गया है, वर्ग ईनम गुप्त रूप से इंट / बूल के लिए परिवर्तनीय नहीं हैं, यह बग्गी कोड से बचने में भी मदद करता है जैसे:

enum MyEnum {
  Value1,
  Value2,
};
...
if (var == Value1 || Value2) // Should be "var == Value2" no error/warning

2
मेरी पिछली टिप्पणी को पूरा करने के लिए, ध्यान दें कि अब gcc के पास एक चेतावनी है -Wint-in-bool-reference, जो इस तरह की त्रुटियों को ठीक से पकड़ लेगी।
अरनौद

0

एक बात जिसका स्पष्ट उल्लेख नहीं किया गया है - गुंजाइश सुविधा आपको एक एनुम और क्लास विधि के लिए एक ही नाम रखने का विकल्प देती है। उदाहरण के लिए:

class Test
{
public:
   // these call ProcessCommand() internally
   void TakeSnapshot();
   void RestoreSnapshot();
private:
   enum class Command // wouldn't be possible without 'class'
   {
        TakeSnapshot,
        RestoreSnapshot
   };
   void ProcessCommand(Command cmd); // signal the other thread or whatever
};
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.