सी ++ में झंडे के रूप में एनम का उपयोग कैसे करें?


187

enumझंडे के रूप में इलाज करना सी # [Flags]विशेषता के माध्यम से अच्छी तरह से काम करता है , लेकिन सी ++ में ऐसा करने का सबसे अच्छा तरीका क्या है?

उदाहरण के लिए, मैं लिखना चाहूंगा:

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};

seahawk.flags = CanFly | EatsFish | Endangered;

हालाँकि, मुझे int/ enumरूपांतरणों के संबंध में संकलक त्रुटियाँ मिलती हैं । क्या सिर्फ ब्लंट कास्टिंग की तुलना में इसे व्यक्त करने का एक अच्छा तरीका है? अधिमानतः, मैं 3 पार्टी पुस्तकालयों जैसे कि बढ़ावा या क्यूटी से निर्माणों पर भरोसा नहीं करना चाहता।

संपादित करें: जैसा कि जवाब में संकेत दिया है, मैं घोषणा करके संकलक त्रुटि से बचने कर सकते हैं seahawk.flagsके रूप में int। हालाँकि, मैं टाइप सुरक्षा को लागू करने के लिए कुछ तंत्र रखना चाहूंगा, ताकि कोई लिख न सके seahawk.flags = HasMaximizeButton


जहाँ तक मुझे दृश्य C ++ 2013 में पता है कि यह [Flags]विशेषता ठीक काम करती है:[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};
rivanov

@rivanov, नहीं, यह C ++ (2015 भी) के साथ काम नहीं करता है। क्या आपका मतलब C # था?
अजय

5
@rivanov, [Flags] विशेषता केवल C ++ CLI में .Net फ्रेमवर्क के साथ काम करती है, देशी C ++ इन विशेषताओं का समर्थन नहीं करता है।
ज़ोल्टन तिरिंडा

जवाबों:


249

"सही" तरीका एनम के लिए बिट ऑपरेटरों को परिभाषित करना है, जैसे:

enum AnimalFlags
{
    HasClaws   = 1,
    CanFly     = 2,
    EatsFish   = 4,
    Endangered = 8
};

inline AnimalFlags operator|(AnimalFlags a, AnimalFlags b)
{
    return static_cast<AnimalFlags>(static_cast<int>(a) | static_cast<int>(b));
}

बाकी बिट संचालक आदि। जरूरत के अनुसार संशोधित करें यदि एनम रेंज इंट रेंज से अधिक है।


42
^ यह। एकमात्र सवाल यह है कि आप ऑपरेटर की परिभाषाओं को स्वचालित / टेम्पलेटेट कैसे करें ताकि आपको हर बार जब आप नई एनम जोड़ते हैं तो उन्हें लगातार परिभाषित करने की आवश्यकता नहीं है।
ईदबश

10
इसके अलावा, क्या मनमाने ढंग से इंट से वापस एनम प्रकार के लिए मान्य है, भले ही इंट मान एनुम के किसी भी पहचानकर्ता के अनुरूप नहीं है?
इंगो शल्क-स्कूप

8
यह पूरी बकवास है। AnimalFlagsअभिव्यक्ति का प्रतिनिधित्व किस सदस्य द्वारा किया जाता है HasClaws | CanFly? यह वह नहीं है जो enumएस के लिए है। पूर्णांक और स्थिरांक का उपयोग करें।
हल्बी की दौड़ ऑर्बिट

26
@LightnessRacesinOrbit: यह सही नहीं है। एक एनुम प्रकार का डोमेन अपने अंतर्निहित प्रकार का डोमेन है - यह केवल यह है कि कुछ लोगों को एक नाम दिया गया है। और आपके प्रश्न का उत्तर देने के लिए: सदस्य " (HasClaws | CanFly)"।
Xeo

5
@MarcusJ: 2 के अधिकारों के लिए अपने मूल्यों को सीमित करने से आपको अपने एनमों को बिट-फ्लैग के रूप में उपयोग करने की अनुमति मिलती है। इस प्रकार यदि आपको एक 3 मिलता है तो आप इसे HasClaws(= 1) और CanFly(= 2) दोनों जानते हैं । यदि इसके बजाय आप केवल 1 को 4 सीधे के माध्यम से मान देते हैं और आपको 3 मिलते हैं, तो यह एकल हो सकता है EatsFish, या फिर HasClawsऔर का संयोजन हो सकता है CanFly। यदि आपकी गणना विशेष राज्यों को निरूपित करती है, तो लगातार मूल्य ठीक हैं, लेकिन झंडे के संयोजन के लिए मूल्यों को थोड़ा-विशिष्ट होना चाहिए।
क्रिश्चियन सेवेरिन

122

नोट (विषय से थोड़ा हटकर भी): अद्वितीय झंडे बनाने का एक और तरीका थोड़ा बदलाव का उपयोग करके किया जा सकता है। मैं, खुद को, पढ़ने में आसान लगता हूं।

enum Flags
{
    A = 1 << 0, // binary 0001
    B = 1 << 1, // binary 0010
    C = 1 << 2, // binary 0100
    D = 1 << 3, // binary 1000
};

यह एक इंट तक मानों को पकड़ सकता है, इसलिए, अधिकांश समय, 32 झंडे जो स्पष्ट रूप से शिफ्ट राशि में परिलक्षित होते हैं।


2
क्या आप कृपया अंतिम कॉमा (3) को हटा सकते हैं और कोड को कॉपी और पेस्ट करना आसान बनाने के लिए} के बाद एक कोलोन जोड़ सकते हैं? धन्यवाद
Katu

4
हेक्सेडेसिमल का कोई उल्लेख नहीं है? ईश - निंदा!
फराप

1
@ जैमी, कार्डिनल्स हमेशा 1 से शुरू होते हैं, केवल ऑर्डिनल्स 0 या 1 से शुरू हो सकते हैं, इस पर निर्भर करता है कि आप किससे बात कर रहे हैं।
माइकल

2
@ मिचेल, यह सच है! एक दुश्मनी में, आप आमतौर पर BLAH_NONE के लिए 0 आरक्षित करते हैं। :-) उस याद को मरोड़ने के लिए धन्यवाद!
जेमी

1
@ कटु • अंतिम गणना पर उत्कृष्ट अल्पविराम मानक द्वारा अनुमत है। मुझे यह पसंद नहीं है, लेकिन मुझे पहले से ही पता है कि स्ट्रॉस्ट्रुप मुझे क्या बताएगा ... "आपको यह पसंद नहीं है? अपनी भाषा बनाने के लिए स्वतंत्र महसूस करें। मैंने किया।"
एलयज

55

मेरे जैसे आलसी लोगों के लिए, यहाँ प्रतिलिपि और पेस्ट करने के लिए अस्थायी समाधान है:

template<class T> inline T operator~ (T a) { return (T)~(int)a; }
template<class T> inline T operator| (T a, T b) { return (T)((int)a | (int)b); }
template<class T> inline T operator& (T a, T b) { return (T)((int)a & (int)b); }
template<class T> inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); }
template<class T> inline T& operator|= (T& a, T b) { return (T&)((int&)a |= (int)b); }
template<class T> inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); }
template<class T> inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); }

23
+1 आलस्य एक प्रोग्रामर के तीन महान गुणों में से एक है: threevirtues.com
फराप

10
यह एक बहुत अच्छा समाधान है, बस सावधान रहें कि यह किसी भी प्रकार के लिए बिटवाइज संचालन प्रदान करेगा। मैं कुछ इसी तरह का उपयोग कर रहा हूं, लेकिन उन लक्षणों की पहचान करने वाले लक्षणों के अलावा, जिनके साथ मैं चाहता हूं कि इसे थोड़ा enable_if जादू के साथ जोड़ा जाए।
राय

@ राई: आप इसे हमेशा किसी नेमस्पेस में रख सकते हैं और usingयह जहां उपयुक्त है, जैसे है rel_ops
याकॉव गल्का

1
@ybungalobill, लेकिन आपको अभी भी परिचालन के दायरे में किसी भी प्रकार के लिए लागू होने वाले संचालन के साथ एक ही समस्या होगी, जो संभवतः एनम से मेल खाएगा? मुझे लगता है कि लक्षण सबसे अधिक आवश्यक हैं।
राय

19
इस कोड का उपयोग न करें। यह किसी भी वर्ग के गलती से संचालित होने का द्वार खोलता है। साथ ही कोड पुरानी शैली की कास्ट का उपयोग कर रहा है जो GCC सख्त संकलन shumshah.com/p/… से नहीं गुजरेगी
शीतल शाह

44

ध्यान दें कि यदि आप विंडोज वातावरण में काम कर रहे हैं, तो DEFINE_ENUM_FLAG_OPERATORSwinnt.h में एक मैक्रो परिभाषित है जो आपके लिए काम करता है। तो इस मामले में, आप यह कर सकते हैं:

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};
DEFINE_ENUM_FLAG_OPERATORS(AnimalFlags)

seahawk.flags = CanFly | EatsFish | Endangered;

44

Seahawk.flags चर किस प्रकार का है?

मानक C ++ में, एन्यूमरेशन टाइप-सुरक्षित नहीं हैं। वे प्रभावी रूप से पूर्णांक हैं।

AnimalFlags आपके चर का प्रकार नहीं होना चाहिए। आपका चर अंतरंग होना चाहिए और त्रुटि दूर हो जाएगी।

कुछ अन्य लोगों की तरह हेक्साडेसिमल मान रखने की आवश्यकता नहीं है। इससे कोई फ़र्क नहीं पड़ता।

एनाम वैल्यू डिफ़ॉल्ट रूप से टाइप इंट के हैं। तो आप निश्चित रूप से बिटवाइज़ कर सकते हैं या उन्हें जोड़ सकते हैं और उन्हें एक साथ रख सकते हैं और परिणाम को एक इंट में स्टोर कर सकते हैं।

Enum प्रकार int का एक सीमित उपसमूह है जिसका मूल्य इसके प्रगणित मूल्यों में से एक है। इसलिए, जब आप उस सीमा के बाहर कुछ नया मूल्य बनाते हैं, तो आप इसे अपने एनम प्रकार के एक चर के बिना असाइन नहीं कर सकते।

अगर आप चाहें तो एनम वैल्यू टाइप भी बदल सकते हैं, लेकिन इस सवाल का कोई मतलब नहीं है।

संपादित करें: पोस्टर ने कहा कि वे प्रकार की सुरक्षा से चिंतित थे और वे ऐसा मान नहीं चाहते हैं जो इंट प्रकार के अंदर मौजूद नहीं होना चाहिए।

लेकिन यह AnimalFlags के एक चर के अंदर AnimalFlags की सीमा के बाहर एक मूल्य रखने के लिए असुरक्षित होगा।

अंतर प्रकार के अंदर रेंज मानों के लिए जाँच करने का एक सुरक्षित तरीका है ...

int iFlags = HasClaws | CanFly;
//InvalidAnimalFlagMaxValue-1 gives you a value of all the bits 
// smaller than itself set to 1
//This check makes sure that no other bits are set.
assert(iFlags & ~(InvalidAnimalFlagMaxValue-1) == 0);

enum AnimalFlags {
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8,

    // put new enum values above here
    InvalidAnimalFlagMaxValue = 16
};

उपरोक्त आपको किसी भिन्न ईनम से अमान्य ध्वज लगाने से नहीं रोकता है जिसका मूल्य 1,2,4, या 8 है।

यदि आप पूर्ण प्रकार की सुरक्षा चाहते हैं, तो आप बस एक std :: सेट कर सकते हैं और वहां प्रत्येक झंडे को संग्रहीत कर सकते हैं। यह अंतरिक्ष कुशल नहीं है, लेकिन यह सुरक्षित है और आपको बिटफ्लैग इंट की तरह ही क्षमता प्रदान करता है।

C ++ 0x नोट: जोरदार टाइप की गई बाते

C ++ 0x में आप अंत में सुरक्षित एनम मान टाइप कर सकते हैं ...।

enum class AnimalFlags {
    CanFly = 2,
    HasClaws = 4
};

if(CanFly == 2) { }//Compiling error

4
एनम मान पूर्णांक नहीं हैं, लेकिन वे बहुत आसानी से पूर्णांक में बदल जाते हैं। के प्रकार के HasClaws | CanFlyकुछ पूर्णांक प्रकार है, लेकिन के प्रकार है HasClawsहै AnimalFlags, न कि एक पूर्णांक प्रकार।
करू

1
आह, लेकिन क्या होगा यदि हम एनम की सही सीमा को केवल व्यक्तिगत ध्वज मानों को ही नहीं बल्कि उनके बिटवाइज़ संयोजनों को भी परिभाषित करते हैं। तब ईडोलोन का उत्तर सही है, और यह सुनिश्चित करता है कि सही ध्वज एनम के केवल संयोजन को उस प्रकार के रूप में पारित किया जा सकता है।
स्कॉट

3
@ संकेत: यह ध्यान देने योग्य है कि सी ++ मानक उस तरह के एक एनुम उदाहरण के मूल्यों की वैध सीमा को परिभाषित करता है। "एक गणना के लिए जहां एमिन सबसे छोटा एन्यूमरेटर है और एमैक्स सबसे बड़ा है, गणना का मान bmax की सीमा में मान है, de ed ned निम्नानुसार है: Let K K एक दो के पूरक प्रतिनिधित्व के लिए 1 और एक के लिए 0 '। पूरक या साइन-परिमाण प्रतिनिधित्व। BMAX से सबसे छोटा मान अधिक है या के बराबर max(|emin| − K, |emax|)और के बराबर (1u<<M) - 1है, जहां Mएक गैर नकारात्मक पूर्णांक है। "
बेन वोइग्ट

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

यह भी ध्यान दें कि C ++ में, नियमित enumरूप से तकनीकी रूप से intअपने अंतर्निहित प्रकार (या तो पूर्व-सी ++ 11 (IIRC), या पोस्ट-सी ++ 11 के रूप में डिफ़ॉल्ट नहीं है जब कोई अंतर्निहित प्रकार निर्दिष्ट नहीं है), हालांकि enum class करता है । इसके बजाय, कुछ बड़ा पर्याप्त करने के लिए अंतर्निहित प्रकार चूक है कि यह तुलना में केवल बड़ा है केवल वास्तविक कठिन नियम के साथ सभी प्रगणक प्रतिनिधित्व करने के लिए, intअगर यह स्पष्ट रूप से की जरूरत किया जाना है। मूल रूप से, अंतर्निहित प्रकार को निर्दिष्ट किया गया है (पैराफ्रास्ड) "जो कुछ भी काम करता है, लेकिन यह संभवतः है int जब तक कि एन्यूमरेटर्स बहुत बड़े नहीं हैं int"।
जस्टिन टाइम -

26

मुझे लगता है कि वर्तमान में ईडोलोन द्वारा स्वीकृत उत्तर बहुत खतरनाक है। कंपाइलर के ऑप्टिमाइज़र संभवत: एनम में संभावित मूल्यों के बारे में धारणा बना सकते हैं और आपको अमान्य मानों के साथ कचरा वापस मिल सकता है। और आमतौर पर कोई भी ध्वज एनम में सभी संभावित क्रमों को परिभाषित नहीं करना चाहता है।

जैसा कि ब्रायन आर। बॉडी नीचे बताता है, यदि आप C ++ 11 का उपयोग कर रहे हैं (जो हर किसी को चाहिए, यह अच्छा है) अब आप इसे आसानी से कर सकते हैं enum class:

enum class ObjectType : uint32_t
{
    ANIMAL = (1 << 0),
    VEGETABLE = (1 << 1),
    MINERAL = (1 << 2)
};


constexpr enum ObjectType operator |( const enum ObjectType selfValue, const enum ObjectType inValue )
{
    return (enum ObjectType)(uint32_t(selfValue) | uint32_t(inValue));
}

// ... add more operators here. 

यह एनम के लिए एक प्रकार को निर्दिष्ट करके एक स्थिर आकार और मूल्य सीमा सुनिश्चित enum classकरता है, उपयोग करके इन्टम्स के स्वचालित डाउनकास्टिंग को रोकता है , और constexprऑपरेटर्स के लिए कोड सुनिश्चित करने के लिए उपयोग करता है और इस प्रकार नियमित संख्याओं के रूप में तेजी से होता है।

पूर्व -11 C ++ बोलियों के साथ अटके हुए लोगों के लिए

अगर मैं एक संकलक के साथ फंस गया था जो C ++ 11 का समर्थन नहीं करता है, तो मैं एक कक्षा में एक इंट-टाइप को लपेटने के साथ जाऊंगा जो तब बिटवाइज ऑपरेटरों के उपयोग की अनुमति देता है और उस मान से इसके मान सेट करने के लिए प्रकार:

template<class ENUM,class UNDERLYING=typename std::underlying_type<ENUM>::type>
class SafeEnum
{
public:
    SafeEnum() : mFlags(0) {}
    SafeEnum( ENUM singleFlag ) : mFlags(singleFlag) {}
    SafeEnum( const SafeEnum& original ) : mFlags(original.mFlags) {}

    SafeEnum&   operator |=( ENUM addValue )    { mFlags |= addValue; return *this; }
    SafeEnum    operator |( ENUM addValue )     { SafeEnum  result(*this); result |= addValue; return result; }
    SafeEnum&   operator &=( ENUM maskValue )   { mFlags &= maskValue; return *this; }
    SafeEnum    operator &( ENUM maskValue )    { SafeEnum  result(*this); result &= maskValue; return result; }
    SafeEnum    operator ~()    { SafeEnum  result(*this); result.mFlags = ~result.mFlags; return result; }
    explicit operator bool()                    { return mFlags != 0; }

protected:
    UNDERLYING  mFlags;
};

आप इसे नियमित रूप से enum + typedef की तरह बहुत अधिक परिभाषित कर सकते हैं:

enum TFlags_
{
    EFlagsNone  = 0,
    EFlagOne    = (1 << 0),
    EFlagTwo    = (1 << 1),
    EFlagThree  = (1 << 2),
    EFlagFour   = (1 << 3)
};

typedef SafeEnum<enum TFlags_>  TFlags;

और उपयोग समान है:

TFlags      myFlags;

myFlags |= EFlagTwo;
myFlags |= EFlagThree;

if( myFlags & EFlagTwo )
    std::cout << "flag 2 is set" << std::endl;
if( (myFlags & EFlagFour) == EFlagsNone )
    std::cout << "flag 4 is not set" << std::endl;

और आप enum foo : typeदूसरे टेम्प्लेट पैरामीटर अर्थात, का उपयोग करके बाइनरी-स्थिर एनम (जैसे C ++ 11 ) के लिए अंतर्निहित प्रकार को भी ओवरराइड कर सकते हैं typedef SafeEnum<enum TFlags_,uint8_t> TFlags;

मैंने operator boolC ++ 11 के explicitकीवर्ड के साथ ओवरराइड को चिह्नित किए जाने से रोकने के लिए इसे अंतरंग रूपांतरण के रूप में चिह्नित किया , क्योंकि वे झंडे के सेट को 0 या 1 में ढहने का कारण बना सकते हैं, जब वे लिखते हैं। यदि आप C ++ 11 का उपयोग नहीं कर सकते हैं, तो उस ओवरलोड को छोड़ दें और उदाहरण के उपयोग में पहले सशर्त को फिर से लिखें (myFlags & EFlagTwo) == EFlagTwo


एक नोट के रूप में, मैं यह सलाह दूंगा कि std::underlying_typeएक विशिष्ट प्रकार के हार्ड-कोडिंग के बजाय प्रारंभ में परिभाषित उदाहरण ऑपरेटर को उपयोग किया जाता है, या यह कि अंतर्निहित प्रकार को प्रदान किया जाता है और सीधे के बजाय एक प्रकार का उपनाम के रूप में उपयोग किया जाता है। इस प्रकार, अंतर्निहित प्रकार में परिवर्तन मैन्युअल रूप से किए जाने के बजाय, स्वचालित रूप से प्रचारित होगा।
जस्टिन टाइम -

17

इसे करने के लिए सबसे आसान तरीका है जैसा कि यहां दिखाया गया है , मानक पुस्तकालय वर्ग बिटसेट का उपयोग करते हुए ।

एक प्रकार से सुरक्षित तरीके से C # सुविधा का अनुकरण करने के लिए, आपको बिटसेट के चारों ओर एक टेम्प्लेट रैपर लिखना होगा, टेम्पलेट के लिए एक प्रकार के पैरामीटर के रूप में दिए गए एनम के साथ int तर्कों की जगह। कुछ इस तरह:

    template <class T, int N>
class FlagSet
{

    bitset<N> bits;

    FlagSet(T enumVal)
    {
        bits.set(enumVal);
    }

    // etc.
};

enum MyFlags
{
    FLAG_ONE,
    FLAG_TWO
};

FlagSet<MyFlags, 2> myFlag;

4
इसे और अधिक पूर्ण कोड के लिए देखें: codereview.stackexchange.com/questions/96146/…
शीतल शाह

11

मेरी राय में अब तक के कोई भी उत्तर आदर्श नहीं हैं। आदर्श होने के लिए मैं समाधान की उम्मीद करूंगा:

  1. समर्थन ==, !=, =, &, &=, |, |=और ~पारंपरिक अर्थों में ऑपरेटरों (यानी a & b)
  2. सुरक्षित रहें अर्थात नॉन-एन्यूमरेटेड वैल्यूज़ जैसे कि शाब्दिक या पूर्णांक प्रकारों को असाइन करने की अनुमति न दें (एन्यूमेरेटेड वैल्यूज़ के बिटवाइज़ कॉम्बिनेशन को छोड़कर) या किसी एन्यूमर वैरिएबल को पूर्णांक प्रकार को असाइन करने की अनुमति दें
  3. परमिट के भाव जैसे if (a & b)...
  4. बुराई मैक्रोज़, कार्यान्वयन विशिष्ट सुविधाओं या अन्य हैक की आवश्यकता नहीं है

अधिकांश समाधान इस प्रकार अब तक अंक 2 या 3 पर गिर जाते हैं। वेबडैंसर मेरी राय में बंद है, लेकिन बिंदु 3 पर विफल रहता है और हर एनम के लिए दोहराया जाना चाहिए।

मेरा प्रस्तावित समाधान WebDancer का एक सामान्यीकृत संस्करण है जो बिंदु 3 को भी संबोधित करता है:

#include <cstdint>
#include <type_traits>

template<typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
class auto_bool
{
    T val_;
public:
    constexpr auto_bool(T val) : val_(val) {}
    constexpr operator T() const { return val_; }
    constexpr explicit operator bool() const
    {
        return static_cast<std::underlying_type_t<T>>(val_) != 0;
    }
};

template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
constexpr auto_bool<T> operator&(T lhs, T rhs)
{
    return static_cast<T>(
        static_cast<typename std::underlying_type<T>::type>(lhs) &
        static_cast<typename std::underlying_type<T>::type>(rhs));
}

template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
constexpr T operator|(T lhs, T rhs)
{
    return static_cast<T>(
        static_cast<typename std::underlying_type<T>::type>(lhs) |
        static_cast<typename std::underlying_type<T>::type>(rhs));
}

enum class AnimalFlags : uint8_t 
{
    HasClaws = 1,
    CanFly = 2,
    EatsFish = 4,
    Endangered = 8
};

enum class PlantFlags : uint8_t
{
    HasLeaves = 1,
    HasFlowers = 2,
    HasFruit = 4,
    HasThorns = 8
};

int main()
{
    AnimalFlags seahawk = AnimalFlags::CanFly;        // Compiles, as expected
    AnimalFlags lion = AnimalFlags::HasClaws;         // Compiles, as expected
    PlantFlags rose = PlantFlags::HasFlowers;         // Compiles, as expected
//  rose = 1;                                         // Won't compile, as expected
    if (seahawk != lion) {}                           // Compiles, as expected
//  if (seahawk == rose) {}                           // Won't compile, as expected
//  seahawk = PlantFlags::HasThorns;                  // Won't compile, as expected
    seahawk = seahawk | AnimalFlags::EatsFish;        // Compiles, as expected
    lion = AnimalFlags::HasClaws |                    // Compiles, as expected
           AnimalFlags::Endangered;
//  int eagle = AnimalFlags::CanFly |                 // Won't compile, as expected
//              AnimalFlags::HasClaws;
//  int has_claws = seahawk & AnimalFlags::CanFly;    // Won't compile, as expected
    if (seahawk & AnimalFlags::CanFly) {}             // Compiles, as expected
    seahawk = seahawk & AnimalFlags::CanFly;          // Compiles, as expected

    return 0;
}

यह आवश्यक ऑपरेटरों का अधिभार बनाता है लेकिन उन्हें सीमित प्रकारों तक सीमित करने के लिए SFINAE का उपयोग करता है। ध्यान दें कि संक्षिप्तता के हितों में मैंने सभी ऑपरेटरों को परिभाषित नहीं किया है, लेकिन केवल यही एक अलग है &। ऑपरेटर वर्तमान में वैश्विक हैं (यानी सभी एन्यूमरेटेड प्रकारों पर लागू होते हैं) लेकिन इसे या तो अधिभार को एक नेमस्पेस (मैं क्या करूँ) में रखकर या अतिरिक्त SFINAE शर्तों को जोड़कर कम किया जा सकता है (शायद विशेष अंतर्निहित प्रकारों का उपयोग करके, या विशेष रूप से प्रकार उपनामों को। )। underlying_type_tएक सी ++ 14 की सुविधा है, लेकिन यह अच्छी तरह से समर्थित किया जा रहा है और एक साधारण के साथ सी ++ 11 के लिए अनुकरण करने के लिए आसान हैtemplate<typename T> using underlying_type_t = underlying_type<T>::type;


जबकि आपका प्रस्तावित समाधान बहुत अच्छा काम करता है, यह इस पैटर्न को उन ईनामों के लिए भी प्रस्तुत करता है, जिन्हें झंडे के रूप में माना नहीं जाता है। संभवतः Microsoft से DEFINE_ENUM_FLAG_OPERATORS जैसे (दुष्ट) मैक्रो के उपयोग का कारण है।
वेब डांसर

@WebDancer, आप निश्चित रूप से सही हैं, लेकिन तब मैंने पहले ही अपने जवाब में कहा था। मैंने मुद्दे को संबोधित करने के दो तरीकों का भी सुझाव दिया - इसे नाम स्थान पर रखना या अधिक प्रतिबंधक SFINAE शर्त का उपयोग करना।
ट्रेवर

मेरी बात यह है कि जब तक आप वास्तव में संकीर्ण नाम स्थान (जैसे नामस्थान AllMyFlagEnums) नहीं बनाते हैं या एक SFINAE शर्त है कि किसी तरह से केवल कुछ ही सटीक चयन करता है कि कोड मेरे दिमाग में टूट गया है। इसे जोखिम में डालने के बजाय, मैं एक "टेक्स्टुअल टेम्प्लेट" को कॉपी और पेस्ट करता हूं, जहां मैं सिर्फ enum नाम की जगह लेता हूं, और कभी-कभी "बुराई" मैक्रोज़। काश एक बेहतर तरीका होता।
वेब डांसर

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

दूसरे, अगर आपको गुंजाइश कम करने की जरूरत है, तो भी नाम स्थान को संकीर्ण होने की आवश्यकता नहीं है - हालांकि यह इस बात पर निर्भर करेगा कि आप क्या कर रहे हैं। यदि आप किसी लाइब्रेरी में काम कर रहे हैं, तो शायद आपके पास पहले से ही अपना कोड है जो किसी नेमस्पेस में एनम पर निर्भर करता है, तो एनम कोड केवल उसी नामस्थान में जाता है। यदि आपको किसी वर्ग के लिए enum व्यवहार की आवश्यकता है (शायद आप enums का उपयोग विधि तर्क या वर्ग के सदस्य चर के रूप में करना चाहते हैं) तो उसी प्रभाव के लिए कक्षा में enum कोड डालें। लब्बोलुआब यह है कि आपको केवल एनम के चारों ओर एक नामस्थान लपेटने की आवश्यकता नहीं है - हालांकि आप कर सकते हैं।
ट्रेवर

8

C ++ मानक स्पष्ट रूप से इस बारे में बात करता है, "17.5.2.1.3 बिटमास्क प्रकार" अनुभाग देखें:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf

आपको मिले इस "टेम्पलेट" को देखते हुए:

enum AnimalFlags : unsigned int
{
    HasClaws = 1,
    CanFly = 2,
    EatsFish = 4,
    Endangered = 8
};

constexpr AnimalFlags operator|(AnimalFlags X, AnimalFlags Y) {
    return static_cast<AnimalFlags>(
        static_cast<unsigned int>(X) | static_cast<unsigned int>(Y));
}

AnimalFlags& operator|=(AnimalFlags& X, AnimalFlags Y) {
    X = X | Y; return X;
}

और अन्य ऑपरेटरों के लिए समान है। "कॉन्स्ट्रेप" पर भी ध्यान दें, यदि आप चाहते हैं कि संचालक संकलित समय को निष्पादित करने में सक्षम हो।

यदि आप C ++ / CLI का उपयोग कर रहे हैं और रेफरी वर्ग के सदस्यों को नियुक्त करने में सक्षम होना चाहते हैं, तो आपको इसके बजाय ट्रैकिंग संदर्भों का उपयोग करना होगा:

AnimalFlags% operator|=(AnimalFlags% X, AnimalFlags Y) {
    X = X | Y; return X;
}

नोट: यह नमूना पूरा नहीं हुआ है, ऑपरेटरों के एक पूरे सेट के लिए "17.5.2.1.3 बिटमास्क प्रकार" अनुभाग देखें।


6

मैंने खुद को एक ही सवाल पूछते हुए पाया और सोरू के समान एक सामान्य सी ++ 11 आधारित समाधान के साथ आया।

template <typename TENUM>
class FlagSet {

private:
    using TUNDER = typename std::underlying_type<TENUM>::type;
    std::bitset<std::numeric_limits<TUNDER>::max()> m_flags;

public:
    FlagSet() = default;

    template <typename... ARGS>
    FlagSet(TENUM f, ARGS... args) : FlagSet(args...)
    {   
        set(f);
    }   
    FlagSet& set(TENUM f)
    {   
        m_flags.set(static_cast<TUNDER>(f));
        return *this;
    }   
    bool test(TENUM f)
    {   
        return m_flags.test(static_cast<TUNDER>(f));
    }   
    FlagSet& operator|=(TENUM f)
    {   
        return set(f);
    }   
};

इंटरफ़ेस को स्वाद के लिए बेहतर बनाया जा सकता है। तो यह इस तरह इस्तेमाल किया जा सकता है:

FlagSet<Flags> flags{Flags::FLAG_A, Flags::FLAG_C};
flags |= Flags::FLAG_D;

2
बेहतर और अधिक पूर्ण कोड के लिए इसे देखें: codereview.stackexchange.com/questions/96146/…
शीतल शाह

5
मेरे पास num_limits के उपयोग को छोड़कर, कोड लगभग समान है। मुझे लगता है कि यह एक सामान्य तरीका है कि एक प्रकार-सुरक्षित एनम वर्ग है। मेरा तर्क है कि हर एनुम के अंत में SENTINEL लगाने से बेहतर है कि num_limits का उपयोग किया जाए।
ओमेयर

1
यह एक विशाल बिटसेट है!
को ऑर्बिट में लाइटनेस की दौड़

(संभावित ...)
कक्षा

5

यदि आपका कंपाइलर अभी तक दृढ़ता से टाइप किए गए एनम का समर्थन नहीं करता है, तो आप c ++ स्रोत से निम्नलिखित लेख को देख सकते हैं :

सार से:

यह आलेख
केवल सुरक्षित और वैध लोगों को अनुमति देने के लिए बिट संचालन को बाधित करने की समस्या का समाधान प्रस्तुत करता है, और सभी अमान्य बिट जोड़तोड़ों को संकलन-समय त्रुटियों में बदल देता है। सबसे अच्छा, बिट ऑपरेशंस का सिंटैक्स अपरिवर्तित रहता है, और बिट्स के साथ काम करने वाले कोड को संशोधित करने की आवश्यकता नहीं है, सिवाय संभवतः उन त्रुटियों को ठीक करने के लिए जो अभी तक अनिर्धारित बनी हुई थीं।


5

मैं निम्नलिखित मैक्रो का उपयोग करता हूं:

#define ENUM_FLAG_OPERATORS(T)                                                                                                                                            \
    inline T operator~ (T a) { return static_cast<T>( ~static_cast<std::underlying_type<T>::type>(a) ); }                                                                       \
    inline T operator| (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) | static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T operator& (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) & static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T operator^ (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) ^ static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T& operator|= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) |= static_cast<std::underlying_type<T>::type>(b) ); }   \
    inline T& operator&= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) &= static_cast<std::underlying_type<T>::type>(b) ); }   \
    inline T& operator^= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) ^= static_cast<std::underlying_type<T>::type>(b) ); }

यह ऊपर वर्णित लोगों के समान है लेकिन इसमें कई सुधार हैं:

  • यह सुरक्षित है (यह नहीं है कि अंतर्निहित प्रकार एक है int)
  • इसे मैन्युअल रूप से अंतर्निहित प्रकार निर्दिष्ट करने की आवश्यकता नहीं है (जैसा कि @LunarEclipse के उत्तर के विपरीत है)

इसमें type_traits शामिल करने की आवश्यकता है:

#include <type_traits>

4

मैं Uliwitness जवाब पर विस्तृत करना चाहता हूं , C ++ 98 के लिए उसका कोड फिक्स करना और C ++ 11 से नीचे C ++ वर्जन में टेम्प्लेट और कीवर्ड की कमी के लिए Safe Bool idiom का उपयोग करना ।std::underlying_type<>explicit

मैंने इसे भी संशोधित किया ताकि एनम मान बिना किसी स्पष्ट कार्य के अनुक्रमिक हो सके, इसलिए आपके पास हो सकता है

enum AnimalFlags_
{
    HasClaws,
    CanFly,
    EatsFish,
    Endangered
};
typedef FlagsEnum<AnimalFlags_> AnimalFlags;

seahawk.flags = AnimalFlags() | CanFly | EatsFish | Endangered;

फिर आप कच्चे झंडे का मूल्य प्राप्त कर सकते हैं

seahawk.flags.value();

यहाँ कोड है।

template <typename EnumType, typename Underlying = int>
class FlagsEnum
{
    typedef Underlying FlagsEnum::* RestrictedBool;

public:
    FlagsEnum() : m_flags(Underlying()) {}

    FlagsEnum(EnumType singleFlag):
        m_flags(1 << singleFlag)
    {}

    FlagsEnum(const FlagsEnum& original):
        m_flags(original.m_flags)
    {}

    FlagsEnum& operator |=(const FlagsEnum& f) {
        m_flags |= f.m_flags;
        return *this;
    }

    FlagsEnum& operator &=(const FlagsEnum& f) {
        m_flags &= f.m_flags;
        return *this;
    }

    friend FlagsEnum operator |(const FlagsEnum& f1, const FlagsEnum& f2) {
        return FlagsEnum(f1) |= f2;
    }

    friend FlagsEnum operator &(const FlagsEnum& f1, const FlagsEnum& f2) {
        return FlagsEnum(f1) &= f2;
    }

    FlagsEnum operator ~() const {
        FlagsEnum result(*this);
        result.m_flags = ~result.m_flags;
        return result;
    }

    operator RestrictedBool() const {
        return m_flags ? &FlagsEnum::m_flags : 0;
    }

    Underlying value() const {
        return m_flags;
    }

protected:
    Underlying  m_flags;
};

3

यदि आप वास्तव में व्यक्तिगत एनम मूल्यों के लिए उपयोग नहीं करते हैं (उदाहरण के लिए, आपको उनमें से स्विच करने की आवश्यकता नहीं है) ... और यदि आप द्विआधारी संगतता बनाए रखने के बारे में चिंतित नहीं हैं, तो आपके लिए यह विकल्प है: आप परवाह मत करो कि आपके बिट्स कहाँ रहते हैं ... जो आप शायद हैं। इसके अलावा आप बेहतर तरीके से स्कूपिंग और एक्सेस कंट्रोल से भी चिंतित नहीं होंगे। हम्म, एनमों में बिट-फील्ड के लिए कुछ अच्छे गुण हैं ... आश्चर्य है कि अगर किसी ने कभी कोशिश की है कि :)

struct AnimalProperties
{
    bool HasClaws : 1;
    bool CanFly : 1;
    bool EatsFish : 1;
    bool Endangered : 1;
};

union AnimalDescription
{
    AnimalProperties Properties;
    int Flags;
};

void TestUnionFlags()
{
    AnimalDescription propertiesA;
    propertiesA.Properties.CanFly = true;

    AnimalDescription propertiesB = propertiesA;
    propertiesB.Properties.EatsFish = true;

    if( propertiesA.Flags == propertiesB.Flags )
    {
        cout << "Life is terrible :(";
    }
    else
    {
        cout << "Life is great!";
    }

    AnimalDescription propertiesC = propertiesA;
    if( propertiesA.Flags == propertiesC.Flags )
    {
        cout << "Life is great!";
    }
    else
    {
        cout << "Life is terrible :(";
    }
}

हम देख सकते हैं कि जीवन महान है, हमारे पास हमारे असतत मूल्य हैं, और हमारे पास & | हमारे दिल की सामग्री, जो अभी भी इसके बिट्स का क्या अर्थ है का संदर्भ है। सब कुछ सुसंगत और पूर्वानुमेय है ... मेरे लिए ... जब तक मैं Microsoft के VC ++ कंपाइलर w / अपडेट 3 का उपयोग Win10 x64 पर करता रहता हूं और अपने कंपाइलर झंडे को नहीं छूता हूं :)

भले ही सब कुछ महान हो ... हमारे पास झंडे के अर्थ के रूप में कुछ संदर्भ हैं, क्योंकि इसके संघ में w / भयानक वास्तविक दुनिया में बिटफील्ड है जहां आपका कार्यक्रम एक से अधिक असतत कार्य के लिए जिम्मेदार हो सकता है अभी भी अकस्मात (काफी आसानी से) अलग-अलग यूनियनों के दो झंडे वाले क्षेत्रों को एक साथ तोड़ते हैं (जैसे, AnimalProperties और ObjectProperties, क्योंकि वे दोनों चींटियां हैं), आपके सभी बिट्स को मिलाते हैं, जो नीचे ट्रेस करने के लिए एक भयानक बग है ... और कैसे वे जानते हैं इस पोस्ट पर बहुत से लोग बहुत बार बिटमास्क के साथ काम नहीं करते हैं, क्योंकि उन्हें बनाना आसान है और उन्हें बनाए रखना कठिन है।

class AnimalDefinition {
public:
    static AnimalDefinition *GetAnimalDefinition( AnimalFlags flags );   //A little too obvious for my taste... NEXT!
    static AnimalDefinition *GetAnimalDefinition( AnimalProperties properties );   //Oh I see how to use this! BORING, NEXT!
    static AnimalDefinition *GetAnimalDefinition( int flags ); //hmm, wish I could see how to construct a valid "flags" int without CrossFingers+Ctrl+Shift+F("Animal*"). Maybe just hard-code 16 or something?

    AnimalFlags animalFlags;  //Well this is *way* too hard to break unintentionally, screw this!
    int flags; //PERFECT! Nothing will ever go wrong here... 
    //wait, what values are used for this particular flags field? Is this AnimalFlags or ObjectFlags? Or is it RuntimePlatformFlags? Does it matter? Where's the documentation? 
    //Well luckily anyone in the code base and get confused and destroy the whole program! At least I don't need to static_cast anymore, phew!

    private:
    AnimalDescription m_description; //Oh I know what this is. All of the mystery and excitement of life has been stolen away :(
}

तो फिर आप "फ़्लैग" की सीधी पहुंच को रोकने के लिए अपनी यूनियन घोषणा को निजी बनाते हैं, और गेटर्स / सेटर और ऑपरेटर ओवरलोड्स को जोड़ना पड़ता है, फिर उस सब के लिए एक मैक्रो बनाते हैं, और आप मूल रूप से ठीक पीछे हैं जहां आपने शुरू किया था जब आपने प्रयास किया था एक Enum के साथ यह करो।

दुर्भाग्य से अगर आप चाहते हैं कि आपका कोड पोर्टेबल हो, तो मुझे नहीं लगता कि ए के पास कोई रास्ता है) बिट लेआउट की गारंटी दें या बी) संकलन समय पर थोड़ा लेआउट निर्धारित करें (ताकि आप इसे ट्रैक कर सकें और कम से कम बदलावों के लिए सही कर सकें संस्करणों / प्लेटफार्मों आदि) बिट क्षेत्रों के साथ एक संरचना में ऑफसेट

रनटाइम पर आप ट्रिक w / फ़ील्ड्स सेट कर सकते हैं और झंडे को XOR कर सकते हैं यह देखने के लिए कि किन बिट्स ने बदलाव किया है, मुझे बहुत भद्दा लगता है हालांकि 100% सुसंगत, प्लेटफ़ॉर्म स्वतंत्र, और पूरी तरह से नियतात्मक समाधान अर्थात: ENUM।

TL; DR: नफरत करने वालों की बात मत सुनो। C ++ अंग्रेजी नहीं है। सिर्फ इसलिए कि C से विरासत में प्राप्त एक संक्षिप्त कीवर्ड की शाब्दिक परिभाषा आपके उपयोग के लायक नहीं है, इसका मतलब यह नहीं है कि आप इसका उपयोग तब नहीं करें जब कीवर्ड की C और C ++ परिभाषा में आपके उपयोग का मामला पूरी तरह से शामिल हो। आप संरचनाओं के अलावा अन्य चीजों के मॉडल और स्कूल और सामाजिक जाति के अलावा अन्य चीजों के लिए कक्षाओं का उपयोग कर सकते हैं। आप फ्लोट का उपयोग उन मूल्यों के लिए कर सकते हैं जो ग्राउंडेड हैं। आप चर का उपयोग कर सकते हैं जो न तो जले हुए हैं और न ही किसी उपन्यास, नाटक या फिल्म में कोई व्यक्ति। कोई भी प्रोग्रामर जो भाषा की युक्ति से पहले किसी कीवर्ड का अर्थ निर्धारित करने के लिए शब्दकोश में जाता है ... अच्छी तरह से मैं अपनी जीभ वहाँ रखूँगा।

यदि आप चाहते हैं कि आपका कोड बोली जाने वाली भाषा के बाद तैयार किया जाए, तो आप ऑब्जेक्टिव-सी में लिखना बंद कर देंगे, जो संयोगवश बिटफिल्ड्स के लिए एनम का भारी उपयोग करता है।


3

केवल सिंटैक्टिक शुगर। कोई अतिरिक्त मेटाडेटा नहीं।

namespace UserRole // grupy
{ 
    constexpr uint8_t dea = 1;
    constexpr uint8_t red = 2;
    constexpr uint8_t stu = 4;
    constexpr uint8_t kie = 8;
    constexpr uint8_t adm = 16;
    constexpr uint8_t mas = 32;
}

इंटीग्रल टाइप पर फ्लैग ऑपरेटर सिर्फ काम करते हैं।


IMHO यह सबसे अच्छा जवाब है। साफ और सरल, आसान क्लाइंट सिंटैक्स। मैं "कॉन्स्ट्रेक्ट uint8_t" के बजाय "कॉन्स्ट इंट" का उपयोग करूंगा, लेकिन यह अवधारणा समान है।
योयो

(क्षमा करें, "constexpr पूर्णांक")
yoyo

3

वर्तमान में एनम फ्लैग के लिए कोई भाषा समर्थन नहीं है, मेटा क्लासेस इस सुविधा को स्वाभाविक रूप से जोड़ सकते हैं यदि यह कभी भी c ++ मानक का हिस्सा होगा।

मेरा समाधान एनम-केवल तात्कालिक टेम्पलेट फ़ंक्शंस बनाने के लिए होगा, जो आपके अंतर्निहित प्रकार का उपयोग करके एनम वर्ग के लिए सुरक्षित-सुरक्षित बिटवाइज़ संचालन के लिए समर्थन जोड़ रहा है:

फ़ाइल: EnumClassBitwise.h

#pragma once
#ifndef _ENUM_CLASS_BITWISE_H_
#define _ENUM_CLASS_BITWISE_H_

#include <type_traits>

//unary ~operator    
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum& operator~ (Enum& val)
{
    val = static_cast<Enum>(~static_cast<std::underlying_type_t<Enum>>(val));
    return val;
}

// & operator
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator& (Enum lhs, Enum rhs)
{
    return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs));
}

// &= operator
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator&= (Enum& lhs, Enum rhs)
{
    lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs));
    return lhs;
}

//| operator

template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator| (Enum lhs, Enum rhs)
{
    return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs));
}
//|= operator

template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum& operator|= (Enum& lhs, Enum rhs)
{
    lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs));
    return lhs;
}

#endif // _ENUM_CLASS_BITWISE_H_

सुविधा के लिए और गलतियों को कम करने के लिए, आप अपने बिट झंडे संचालन को एनम के लिए और पूर्णांक के लिए भी लपेट सकते हैं:

फ़ाइल: BitFlags.h

#pragma once
#ifndef _BIT_FLAGS_H_
#define _BIT_FLAGS_H_

#include "EnumClassBitwise.h"

 template<typename T>
 class BitFlags
 {
 public:

     constexpr inline BitFlags() = default;
     constexpr inline BitFlags(T value) { mValue = value; }
     constexpr inline BitFlags operator| (T rhs) const { return mValue | rhs; }
     constexpr inline BitFlags operator& (T rhs) const { return mValue & rhs; }
     constexpr inline BitFlags operator~ () const { return ~mValue; }
     constexpr inline operator T() const { return mValue; }
     constexpr inline BitFlags& operator|=(T rhs) { mValue |= rhs; return *this; }
     constexpr inline BitFlags& operator&=(T rhs) { mValue &= rhs; return *this; }
     constexpr inline bool test(T rhs) const { return (mValue & rhs) == rhs; }
     constexpr inline void set(T rhs) { mValue |= rhs; }
     constexpr inline void clear(T rhs) { mValue &= ~rhs; }

 private:
     T mValue;
 };
#endif //#define _BIT_FLAGS_H_

संभव उपयोग:

#include <cstdint>
#include <BitFlags.h>
void main()
{
    enum class Options : uint32_t
    { 
          NoOption = 0 << 0
        , Option1  = 1 << 0
        , Option2  = 1 << 1
        , Option3  = 1 << 2
        , Option4  = 1 << 3
    };

    const uint32_t Option1 = 1 << 0;
    const uint32_t Option2 = 1 << 1;
    const uint32_t Option3 = 1 << 2;
    const uint32_t Option4 = 1 << 3;

   //Enum BitFlags
    BitFlags<Options> optionsEnum(Options::NoOption);
    optionsEnum.set(Options::Option1 | Options::Option3);

   //Standard integer BitFlags
    BitFlags<uint32_t> optionsUint32(0);
    optionsUint32.set(Option1 | Option3); 

    return 0;
}

3

@Xaqq ने एक वर्ग द्वारा यहाँ बहुत अच्छे टाइप के सुरक्षित तरीके इस्तेमाल किए हैं flag_set

मैंने GitHub में कोड प्रकाशित किया , उपयोग इस प्रकार है:

#include "flag_set.hpp"

enum class AnimalFlags : uint8_t {
    HAS_CLAWS,
    CAN_FLY,
    EATS_FISH,
    ENDANGERED,
    _
};

int main()
{
    flag_set<AnimalFlags> seahawkFlags(AnimalFlags::HAS_CLAWS
                                       | AnimalFlags::EATS_FISH
                                       | AnimalFlags::ENDANGERED);

    if (seahawkFlags & AnimalFlags::ENDANGERED)
        cout << "Seahawk is endangered";
}

2

आप वस्तुओं और वस्तुओं के संग्रह को भ्रमित कर रहे हैं। विशेष रूप से, आप बाइनरी फ़्लैग को बाइनरी फ़्लैग के सेट के साथ भ्रमित कर रहे हैं। एक उचित समाधान इस तरह दिखेगा:

// These are individual flags
enum AnimalFlag // Flag, not Flags
{
    HasClaws = 0,
    CanFly,
    EatsFish,
    Endangered
};

class AnimalFlagSet
{
    int m_Flags;

  public:

    AnimalFlagSet() : m_Flags(0) { }

    void Set( AnimalFlag flag ) { m_Flags |= (1 << flag); }

    void Clear( AnimalFlag flag ) { m_Flags &= ~ (1 << flag); }

    bool Get( AnimalFlag flag ) const { return (m_Flags >> flag) & 1; }

};

2

ओवरलोडिंग या कास्टिंग के किसी भी गुच्छा की आवश्यकता के बिना यहां मेरा समाधान है:

namespace EFoobar
{
    enum
    {
        FB_A    = 0x1,
        FB_B    = 0x2,
        FB_C    = 0x4,
    };
    typedef long Flags;
}

void Foobar(EFoobar::Flags flags)
{
    if (flags & EFoobar::FB_A)
        // do sth
        ;
    if (flags & EFoobar::FB_B)
        // do sth
        ;
}

void ExampleUsage()
{
    Foobar(EFoobar::FB_A | EFoobar::FB_B);
    EFoobar::Flags otherflags = 0;
    otherflags|= EFoobar::FB_B;
    otherflags&= ~EFoobar::FB_B;
    Foobar(otherflags);
}

मुझे लगता है कि यह ठीक है, क्योंकि हम पहचानते हैं (गैर-दृढ़ता से टाइप किए गए) और वैसे भी इनम।

बस के रूप में (अब) साइड नोट, यदि आप

  • दृढ़ता से टाइप किए गए enums और का उपयोग करना चाहते हैं
  • अपने झंडे के साथ भारी फिडलिंग की जरूरत नहीं है
  • प्रदर्शन कोई मुद्दा नहीं है

मैं इसके साथ आऊंगा:

#include <set>

enum class EFoobarFlags
{
    FB_A = 1,
    FB_B,
    FB_C,
};

void Foobar(const std::set<EFoobarFlags>& flags)
{
    if (flags.find(EFoobarFlags::FB_A) != flags.end())
        // do sth
        ;
    if (flags.find(EFoobarFlags::FB_B) != flags.end())
        // do sth
        ;
}

void ExampleUsage()
{
    Foobar({EFoobarFlags::FB_A, EFoobarFlags::FB_B});
    std::set<EFoobarFlags> otherflags{};
    otherflags.insert(EFoobarFlags::FB_B);
    otherflags.erase(EFoobarFlags::FB_B);
    Foobar(otherflags);
}

C ++ 11 इनिशलाइज़र सूचियों का उपयोग करना और enum class


वैसे, मैं झंडे के लिए किसी भी तरह की सिफारिश नहीं करूंगा। सरल कारण: झंडे का संयोजन फिर से एनम के तत्व नहीं हैं। इसलिए यह काफी अनुपयुक्त लगता है। वैकल्पिक रूप से मैं using Flags = unsigned longएक नेमस्पेस या संरचना का उपयोग करूँगा जिसमें ध्वज के मान स्वयं शामिल होंगे /*static*/ const Flags XY = 0x01और इसी तरह।
yau

1

जैसा कि ऊपर (काई) या निम्नलिखित करें। वास्तव में "एनुमरेशन्स" हैं, आप जो करना चाहते हैं, उसके लिए एक सेट है, इसलिए आपको वास्तव में स्टाल सेट का उपयोग करना चाहिए

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};

int main(void)
{
    AnimalFlags seahawk;
    //seahawk= CanFly | EatsFish | Endangered;
    seahawk= static_cast<AnimalFlags>(CanFly | EatsFish | Endangered);
}

1

शायद उद्देश्य-सी के NS_OPTIONS की तरह।

#define ENUM(T1, T2) \
enum class T1 : T2; \
inline T1 operator~ (T1 a) { return (T1)~(int)a; } \
inline T1 operator| (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) | static_cast<T2>(b))); } \
inline T1 operator& (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) & static_cast<T2>(b))); } \
inline T1 operator^ (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) ^ static_cast<T2>(b))); } \
inline T1& operator|= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) |= static_cast<T2>(b))); } \
inline T1& operator&= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) &= static_cast<T2>(b))); } \
inline T1& operator^= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) ^= static_cast<T2>(b))); } \
enum class T1 : T2

ENUM(Options, short) {
    FIRST  = 1 << 0,
    SECOND = 1 << 1,
    THIRD  = 1 << 2,
    FOURTH = 1 << 3
};

auto options = Options::FIRST | Options::SECOND;
options |= Options::THIRD;
if ((options & Options::SECOND) == Options::SECOND)
    cout << "Contains second option." << endl;
if ((options & Options::THIRD) == Options::THIRD)
    cout << "Contains third option." << endl;
return 0;

// Output:
// Contains second option. 
// Contains third option.

क्या आप बता सकते हैं कि आपका उत्तर सबसे उपयुक्त क्यों है? कई अन्य उत्तर हैं जिन्होंने इस प्रश्न का उत्तर दिया है, इसलिए कृपया अपनी अंतर बताने के लिए कुछ जानकारी शामिल करें।
ट्रेवॉर्प
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.