आगे C ++ में एनम घोषित करते हुए


263

मैं निम्नलिखित की तरह कुछ करने की कोशिश कर रहा हूँ:

enum E;

void Foo(E e);

enum E {A, B, C};

जो संकलक अस्वीकार करता है। मैंने Google पर त्वरित रूप से देखा है और सर्वसम्मति से लगता है कि "आप ऐसा नहीं कर सकते", लेकिन मैं यह नहीं समझ सकता कि क्यों। क्या कोई समझा सकता है?

स्पष्टीकरण 2: मैं ऐसा कर रहा हूं क्योंकि मेरे पास एक वर्ग में निजी विधियां हैं जो कहा जाता है कि एनम, और मैं नहीं चाहता कि एनम के मूल्यों को उजागर किया जाए - इसलिए, उदाहरण के लिए, मैं नहीं चाहता कि किसी को पता चले कि ई को परिभाषित किया गया है।

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

जैसा कि प्रोजेक्ट एक्स कुछ ऐसा नहीं है जो मैं चाहता हूं कि मेरे उपयोगकर्ता इसके बारे में जानें।

इसलिए, मैं एनम को आगे घोषित करना चाहता था, इसलिए मैं हेडर फ़ाइल में निजी तरीके डाल सकता हूं, एनम को आंतरिक रूप से सीपीपी में घोषित कर सकता हूं, और निर्मित लाइब्रेरी फ़ाइल और हेडर को लोगों में वितरित कर सकता हूं।

संकलक के लिए - यह जीसीसी है।


इतने सालों में और किसी तरह स्टैकऑवरफ्लो ने मुझे वापस कर दिया;) एक पोस्टमॉर्टम सुझाव के रूप में - बस विशेष रूप से आपके द्वारा वर्णित परिदृश्य में ऐसा न करें । मैं एक अमूर्त इंटरफ़ेस को परिभाषित करना पसंद करूंगा और इस यूजर को बेनकाब करूंगा और वह इनम की परिभाषा और अन्य सभी कार्यान्वयन विवरणों को आंतरिक कार्यान्वयन के साथ रखेगा जो कोई भी मेरी तरफ नहीं देखता है जो मुझे जब भी करने की अनुमति देता है और जब उपयोगकर्ता देखते हैं तो उसका पूर्ण नियंत्रण होता है। कुछ भी।
RnR

जवाबों:


216

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


ISO C ++ मानक की धारा 7.2.5 से:

अंतर्निहित प्रकार एक गणन का एक अभिन्न प्रकार है कि सभी प्रगणक गणना में परिभाषित मूल्यों का प्रतिनिधित्व कर सकते हैं। यह कार्यान्वयन से परिभाषित जो अभिन्न प्रकार सिवाय इसके कि अंतर्निहित प्रकार से बड़ा नहीं होगा एक गणन के लिए अंतर्निहित प्रकार के रूप में इस्तेमाल किया जाता है intजब तक कि एक प्रगणक का मूल्य एक में फिट नहीं कर सकते intया unsigned int। यदि एन्यूमरेटर-लिस्ट खाली है, तो अंतर्निहित प्रकार ऐसा है जैसे एन्यूमरेशन में वैल्यू के साथ एक एन्यूमरेटर होता है। sizeof()एन्यूमरेशन टाइप पर लागू किया गया मान , एन्यूमरेशन टाइप का ऑब्जेक्ट या एन्यूमरेटर, पर sizeof()लागू मूल्य होता है। अंतर्निहित प्रकार।

चूंकि फ़ंक्शन को कॉलर को कॉल स्टैक को सही ढंग से सेटअप करने के लिए मापदंडों के आकार को जानना चाहिए, इसलिए फ़ंक्शन प्रोटोटाइप से पहले एक एन्यूमरेशन सूची में गणना की संख्या ज्ञात होनी चाहिए।

अद्यतन: C ++ 0X में भविष्यवाणियां घोषित करने के लिए एक वाक्यविन्यास प्रस्तावित किया गया है और उसे स्वीकार किया गया है। आप प्रस्ताव को http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf पर देख सकते हैं


29
-1। आपका तर्क सही नहीं हो सकता है - अन्यथा, आपको "क्लास सी" घोषित करने की अनुमति क्यों है? और फिर एक फ़ंक्शन प्रोटोटाइप की घोषणा करें जो सी को पूरी तरह से परिभाषित करने से पहले सी लेता है या वापस करता है?
j_random_hacker

112
@j_random: आप पूरी तरह से परिभाषित होने से पहले एक वर्ग का उपयोग नहीं कर सकते हैं - आप केवल एक संकेतक या उस वर्ग के संदर्भ का उपयोग कर सकते हैं और ऐसा इसलिए है क्योंकि उनका आकार और संचालन के तरीके इस बात पर निर्भर नहीं करते हैं कि वर्ग क्या है।
RnR

27
एक वर्ग वस्तु के लिए एक संदर्भ या सूचक का आकार संकलक द्वारा निर्धारित किया जाता है, और वस्तु के वास्तविक आकार से स्वतंत्र है - यह संकेत और संदर्भ का आकार है। एनम एक ऑब्जेक्ट है, और कंपाइलर को सही स्टोरेज एक्सेस करने के लिए इसके आकार की आवश्यकता होती है।
KJAWolf

16
तार्किक रूप से यह इंगित करने में सक्षम होगा कि अगर हम कक्षाओं के माध्यम से कर सकते हैं, तो हम आगे की घोषणा करने वाले एनमों को संदर्भित करें। यह सिर्फ इतना है कि आप अक्सर नुमाइंदों से दुश्मनी करने से बाज नहीं आते हैं :)
पावेल मिनावे

20
मुझे पता है कि यह चर्चा बहुत पहले समाप्त हो गई थी, लेकिन मुझे यहाँ @j_random_hacker के साथ लाइन अप करना होगा: यहाँ समस्या सूचक या अधूरे प्रकारों के संदर्भ में नहीं है, बल्कि घोषणाओं में अपूर्ण प्रकारों के उपयोग के बारे में है। चूंकि यह करना कानूनी है struct S; void foo(S s);(ध्यान दें कि fooकेवल घोषित है, परिभाषित नहीं है), तो कोई कारण नहीं है कि हम ऐसा नहीं कर सकते enum E; void foo(E e);। दोनों ही मामलों में, आकार की आवश्यकता नहीं है।
ल्यूक टॉरिल

198

सी ++ 11 के बाद से एनमों की फॉरवर्ड घोषणा संभव है। पहले, कारण एनुम प्रकारों को आगे घोषित नहीं किया जा सकता था क्योंकि गणना का आकार इसकी सामग्री पर निर्भर करता है। जब तक एन्यूमरेशन का आकार एप्लिकेशन द्वारा निर्दिष्ट किया जाता है, तब तक इसे आगे घोषित किया जा सकता है:

enum Enum1;                   //Illegal in C++ and C++0x; no size is explicitly specified.
enum Enum2 : unsigned int;    //Legal in C++0x.
enum class Enum3;             //Legal in C++0x, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; //Legal C++0x.
enum Enum2 : unsigned short;  //Illegal in C++0x, because Enum2 was previously declared with a different type.

1
क्या इस सुविधा के लिए कोई संकलक समर्थन है? GCC 4.5 के लिए यह प्रतीत नहीं होता है :(
rubenvb 16

4
@rubenvb तो विजुअल C ++ 11 (2012) blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
knatten

मैं enum32_t की तलाश कर रहा था और आपके उत्तर के साथ enum XXX: uint32_t {a, b, c};
फंतासी

मैंने सोचा कि स्कूप्ड एनम (एनम वर्ग) को C ++ 11 में लागू किया गया था? यदि हां, तो वे C ++ 0X में कैसे कानूनी हैं?
टेराबिट्स

1
C ++ 0x C ++ 11, @Terrabits के लिए काम करने का नाम था, इससे पहले इसे आधिकारिक तौर पर मानकीकृत किया गया था। तर्क यह है कि यदि किसी सुविधा को (या अत्यधिक संभावना) एक अद्यतन मानक में शामिल करने के लिए जाना जाता है, तो मानक से पहले उस सुविधा का उपयोग आधिकारिक रूप से जारी किया जाता है जो काम करने वाले नाम का उपयोग करता है। (जैसे, कंपाइलर्स जिन्होंने 2011 में आधिकारिक मानकीकरण से पहले C ++ 11 फीचर्स का समर्थन किया था, C ++ 0x सपोर्ट था, कंपाइलर्स जिन्होंने आधिकारिक मानकीकरण से पहले C ++ 17 फीचर्स का समर्थन किया था, और C ++ 20 फीचर्स का समर्थन करने वाले कंपाइलर्स अभी (2019) को C ++ 2a सपोर्ट है।)
जस्टिन टाइम - मोनिका

79

मैं हाल के घटनाक्रमों को देखते हुए अप-टू-डेट उत्तर जोड़ रहा हूं।

आप C ++ 11 में एक एनम को फॉरवर्ड-डिक्लेयर कर सकते हैं, इसलिए जब तक आप एक ही समय में इसके स्टोरेज टाइप को घोषित करते हैं। वाक्य विन्यास इस तरह दिखता है:

enum E : short;
void foo(E e);

....

enum E : short
{
    VALUE_1,
    VALUE_2,
    ....
}

वास्तव में, यदि फ़ंक्शन एन्यूमरेशन के मानों को संदर्भित नहीं करता है, तो आपको उस बिंदु पर पूर्ण घोषणा की आवश्यकता नहीं है।

यह G ++ 4.6 और उसके बाद ( -std=c++0xया -std=c++11अधिक हाल के संस्करणों में) द्वारा समर्थित है । विजुअल C ++ 2013 इसका समर्थन करता है; पहले के संस्करणों में इसके कुछ प्रकार के गैर-मानक समर्थन हैं जो मुझे अभी तक समझ नहीं आए हैं - मुझे कुछ सुझाव मिले कि एक साधारण आगे की घोषणा कानूनी है, लेकिन YMMV।


4
+1 क्योंकि यह एकमात्र उत्तर है जिसमें उल्लेख किया गया है कि आपको अपनी घोषणा के साथ-साथ अपनी परिभाषा में भी प्रकार की घोषणा करने की आवश्यकता है।
तुरोनी

मेरा मानना ​​है कि प्रारंभिक MSVC में आंशिक समर्थन C ++ / CLI से enum classC ++ एक्सटेंशन (C ++ 11 के अलग होने से पहले enum class) के रूप में, कम से कम अगर मुझे सही ढंग से याद है तो वापस भेज दिया गया था। संकलक ने आपको एक एनम के अंतर्निहित प्रकार को निर्दिष्ट करने की अनुमति दी, लेकिन समर्थन enum classया अग्रेषित-घोषित एनमों का समर्थन नहीं किया , और आपको चेतावनी दी कि एनम के दायरे के साथ एक एन्यूमर को अर्हता प्राप्त करना एक गैर-मानक विस्तार था। मुझे याद है कि यह C ++ 11 में अंतर्निहित प्रकार को निर्दिष्ट करने के समान ही काम कर रहा है, और अधिक कष्टप्रद को छोड़कर, क्योंकि आपको चेतावनी को दबाना था।
जस्टिन टाइम -

30

C ++ में चीजों को अग्रेषित करना बहुत उपयोगी है क्योंकि यह नाटकीय रूप से संकलन समय को गति देता है । आप आगे सहित सी में कई चीजें ++ घोषणा कर सकते हैं: struct, class, function, आदि ...

लेकिन क्या आप आगे enumC ++ में घोषणा कर सकते हैं ?

नहीं, तुम नहीं कर सकते।

लेकिन इसकी अनुमति क्यों नहीं दी गई? यदि इसकी अनुमति थी तो आप enumअपने हेडर फ़ाइल में अपने प्रकार को परिभाषित कर सकते हैं , और enumआपके स्रोत फ़ाइल में आपके मूल्यों को। लगता है कि यह सही अनुमति दी जानी चाहिए?

गलत।

C ++ में कोई डिफ़ॉल्ट प्रकार नहीं है enumजैसे कि C # (int) में है। C ++ में आपका enumप्रकार कंपाइलर द्वारा निर्धारित किया जाएगा किसी भी प्रकार का होगा जो आपके लिए आपके द्वारा निर्धारित मानों की श्रेणी में फिट होगा enum

इसका क्या मतलब है?

इसका अर्थ है कि आपके enumअंतर्निहित प्रकार पूरी तरह से निर्धारित नहीं किए जा सकते हैं जब तक कि आपके पास enumपरिभाषित के सभी मूल्य नहीं हैं । आप किस आदमी को अपनी घोषणा और परिभाषा अलग नहीं कर सकते enum। और इसलिए आप enumC ++ में घोषणा नहीं कर सकते ।

ISO C ++ मानक S7.2.5:

एन्यूमरेशन का अंतर्निहित प्रकार एक अभिन्न प्रकार है जो एन्यूमरेशन में परिभाषित सभी एन्यूमरेटर मानों का प्रतिनिधित्व कर सकता है। यह कार्यान्वयन से परिभाषित जो अभिन्न प्रकार सिवाय इसके कि अंतर्निहित प्रकार से बड़ा नहीं होगा एक गणन के लिए अंतर्निहित प्रकार के रूप में इस्तेमाल किया जाता है intजब तक कि एक प्रगणक का मूल्य एक में फिट नहीं कर सकते intया unsigned int। यदि एन्यूमरेटर-लिस्ट खाली है, तो अंतर्निहित प्रकार ऐसा है जैसे एन्यूमरेशन में वैल्यू के साथ एक एन्यूमरेटर होता है। sizeof()एन्यूमरेशन टाइप पर लागू किया गया मान , एन्यूमरेशन टाइप का ऑब्जेक्ट या एन्यूमरेटर, पर sizeof()लागू मूल्य होता है। अंतर्निहित प्रकार।

आप sizeofऑपरेटर का उपयोग करके C ++ में एनुमरेटेड प्रकार का आकार निर्धारित कर सकते हैं । प्रगणित प्रकार का आकार इसके अंतर्निहित प्रकार का आकार है। इस प्रकार आप अनुमान लगा सकते हैं कि आपका कंपाइलर आपके लिए किस प्रकार का उपयोग कर रहा है enum

यदि आप इस प्रकार enumस्पष्ट रूप से अपने प्रकार को निर्दिष्ट करते हैं :

enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);

क्या आप आगे अपनी घोषणा कर सकते हैं enum?

नहीं, लेकिन क्यों नहीं?

एक के प्रकार को निर्दिष्ट करना enumवास्तव में वर्तमान सी ++ मानक का हिस्सा नहीं है। यह एक VC ++ एक्सटेंशन है। यह हालांकि C ++ 0x का हिस्सा होगा।

स्रोत


14
यह उत्तर अब कई वर्षों पुराना है।
टॉम

समय हम सभी को मूर्ख बनाता है। आपकी टिप्पणी अब कई वर्षों पुरानी है; एक दशक का जवाब!
pjcard

14

[मेरा उत्तर गलत है, लेकिन मैंने इसे यहाँ छोड़ दिया है क्योंकि टिप्पणियाँ उपयोगी हैं]।

आगे की घोषणा करने वाले एनमोज़ गैर-मानक हैं, क्योंकि विभिन्न एनम प्रकारों के संकेत एक ही आकार के होने की गारंटी नहीं है। कंपाइलर को यह जानने के लिए परिभाषा देखने की आवश्यकता हो सकती है कि इस प्रकार के साथ किस आकार के पॉइंटर्स का उपयोग किया जा सकता है।

व्यवहार में, कम से कम सभी लोकप्रिय संकलकों पर, इशारा करने वाले लोग एक सुसंगत आकार हैं। उदाहरण के लिए, विज़ुअल सी ++ द्वारा भाषा विस्तार के लिए घोषणा की गई है।


2
-1। यदि आपका तर्क सही था, तो उसका तर्क यह होगा कि वर्ग प्रकारों की आगे की घोषणाओं का उपयोग उन प्रकारों के लिए संकेत बनाने के लिए नहीं किया जा सकता है - लेकिन वे कर सकते हैं।
j_random_hacker

6
+1। रीजनिंग सही है। विशिष्ट मामला प्लेटफ़ॉर्म है जहाँ आकार (चार *)> आकार (इंट *)। दोनों एक प्रकार के लिए, श्रेणी के आधार पर अंतर्निहित प्रकार हो सकते हैं। वर्गों में अंतर्निहित प्रकार नहीं होते हैं इसलिए उपमा झूठी होती है।
MSalters

3
@ संदेश: उदाहरण: "संरचना S {int x;};" अब, sizeof (S *) किसी भी अन्य पॉइंटर-टू-स्ट्रक्चर के आकार के बराबर होना चाहिए, क्योंकि C ++ इस तरह के पॉइंटर को S की परिभाषा से पहले घोषित और उपयोग करने की अनुमति देता है ...
j_random_hacker

1
@MSalters: ... एक ऐसे मंच पर जहां आकार (चार *)> आकार (इंट *), इस विशेष संरचना के लिए इस तरह के "पूर्ण आकार" सूचक का उपयोग अक्षम हो सकता है, लेकिन यह नाटकीय रूप से कोडिंग को सरल करता है - और बिल्कुल वैसा ही बात हो सकती है, और चाहिए, enum प्रकार के लिए किया जाना चाहिए।
j_random_hacker

4
डेटा और पॉइंटर्स टू फंक्शन के पॉइंटर्स अलग-अलग साइज़ के हो सकते हैं, लेकिन मुझे पूरा यकीन है कि डेटा पॉइंटर्स को राउंड-ट्रिप (किसी दूसरे डेटा पॉइंटर टाइप पर कास्ट करना, फिर ऑरिजिनल पर वापस जाना, अभी भी काम करने की ज़रूरत है), जिसका मतलब है कि सभी डेटा पॉइंटर्स एक ही आकार के होते हैं।
बेन वोइगट

7

वास्तव में एनम की आगे की घोषणा के रूप में ऐसी कोई बात नहीं है। जैसा कि एक एनुम की परिभाषा में ऐसा कोई कोड नहीं है जो एनम का उपयोग करके अन्य कोड पर निर्भर हो सकता है, यह आमतौर पर एनम को पूरी तरह से परिभाषित करने के लिए एक समस्या नहीं है जब आप पहली बार इसे घोषित कर रहे हैं।

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

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


5

बस यह देखते हुए कि वास्तव में कारण यह है कि आगे की घोषणा के बाद एनम का आकार अभी तक ज्ञात नहीं है। ठीक है, आप एक संरचना के आगे की घोषणा का उपयोग करते हुए एक पॉइंटर को पास करने में सक्षम होते हैं या किसी ऑब्जेक्ट को उस स्थान से संदर्भित करते हैं जिसे आगे घोषित संरचना परिभाषा में भी संदर्भित किया जाता है।

फॉरवर्ड घोषित करने के लिए एनम बहुत उपयोगी नहीं होगा, क्योंकि कोई एनम बाय-वैल्यू के आस-पास से गुजरने में सक्षम होगा। आपके पास इसका कोई संकेतक भी नहीं हो सकता है, क्योंकि मैंने हाल ही में कहा है कि कुछ प्लेटफार्मों ने इंट या लंबे समय के लिए चार आकार के पॉइंटर्स का उपयोग किया है। तो यह सब एनम की सामग्री पर निर्भर करता है।

वर्तमान सी ++ मानक स्पष्ट रूप से कुछ करना पसंद करता है

enum X;

(में 7.1.5.3/1)। लेकिन अगले साल के कारण अगले सी ++ मानक निम्नलिखित की अनुमति देता है, जिसने मुझे आश्वस्त किया कि समस्या वास्तव में अंतर्निहित प्रकार के साथ क्या करना है:

enum X : int;

इसे "अपारदर्शी" एनम घोषणा के रूप में जाना जाता है। तुम भी निम्नलिखित कोड में मूल्य से एक्स का उपयोग कर सकते हैं । और इसके प्रगणकों को बाद में गणना के बाद के पुनर्वितरण में परिभाषित किया जा सकता है। 7.2वर्तमान कार्य मसौदे में देखें ।


4

मैं इसे इस तरह से करूँगा:

[सार्वजनिक हेडर में]

typedef unsigned long E;

void Foo(E e);

[आंतरिक हेडर में]

enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
  FORCE_32BIT = 0xFFFFFFFF };

FORCE_32BIT को जोड़कर हम यह सुनिश्चित करते हैं कि Econtent लंबे समय तक संकलित रहे, इसलिए यह E के साथ विनिमेय है।


1
बेशक, इसका मतलब यह है कि (ए) ई और ईकोन्टेंट के प्रकार अलग-अलग हैं, और (बी) एलपी 64 प्रणालियों पर, आकार (ई) = 2 * आकार (ईसीओन्ट)। तुच्छ निर्धारण: ULONG_MAX, साथ ही साथ पढ़ना आसान है।
MSalters

2

यदि आप वास्तव में नहीं चाहते हैं कि आपकी ईनाम आपकी हेडर फ़ाइल में दिखाई दे और यह सुनिश्चित करें कि इसका उपयोग केवल निजी विधियों द्वारा किया जाता है, तो एक समाधान pimpl सिद्धांत के साथ जा सकता है।

यह एक ऐसी तकनीक है जो सिर्फ घोषित करके हेडर में क्लास इंटर्नल्स को छिपाना सुनिश्चित करती है:

class A 
{
public:
    ...
private:
    void* pImpl;
};

फिर आपकी कार्यान्वयन फ़ाइल (सीपीपी) में, आप एक वर्ग की घोषणा करते हैं जो आंतरिक लोगों का प्रतिनिधित्व करेगा।

class AImpl
{
public:
    AImpl(A* pThis): m_pThis(pThis) {}

    ... all private methods here ...
private:
    A* m_pThis;
};

आपको क्लास कंस्ट्रक्टर में गतिशील रूप से कार्यान्वयन बनाना होगा और इसे विध्वंसक में हटाना होगा और सार्वजनिक विधि को लागू करते समय, आपको इसका उपयोग करना होगा:

((AImpl*)pImpl)->PrivateMethod();

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

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


3
संरचना AImpl; संरचना A {निजी: AImpl * pImpl; };

2

आप कुछ रचनाकारों और प्रकार के रूपांतरणों को जोड़ते हुए, एनुम को एक संरचना में लपेट सकते हैं, और आगे के बजाय संरचना को घोषित कर सकते हैं।

#define ENUM_CLASS(NAME, TYPE, VALUES...) \
struct NAME { \
    enum e { VALUES }; \
    explicit NAME(TYPE v) : val(v) {} \
    NAME(e v) : val(v) {} \
    operator e() const { return e(val); } \
    private:\
        TYPE val; \
}

यह काम करता प्रतीत होता है: http://ideone.com/TYtP2


1

लगता है कि इसे जीसीसी में आगे घोषित नहीं किया जा सकता है!

दिलचस्प चर्चा यहाँ


1

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

सबसे पहले, dcl.enum से, खंड 7.2:

एन्यूमरेशन का अंतर्निहित प्रकार एक अभिन्न प्रकार है जो एन्यूमरेशन में परिभाषित सभी एन्यूमरेटर मानों का प्रतिनिधित्व कर सकता है। यह कार्यान्वयन-परिभाषित है जो एक अभिन्न प्रकार के लिए अंतर्निहित प्रकार के रूप में अभिन्न प्रकार का उपयोग किया जाता है सिवाय इसके कि अंतर्निहित प्रकार इंट से बड़ा नहीं होगा जब तक कि एक एन्यूमरेटर का मान किसी इंट या अहस्ताक्षरित इंट में फिट नहीं हो सकता। यदि एन्यूमरेटर-सूची खाली है, तो अंतर्निहित प्रकार ऐसा है जैसे एन्यूमरेशन में वैल्यू 0. के साथ एक एन्यूमरेटर होता है। एन्यूमरेशन प्रकार पर लागू आकार का आकार (), एन्यूमरेशन टाइप का ऑब्जेक्ट या एन्यूमरेटर, का मान होता है। sizeof () अंतर्निहित प्रकार पर लागू होता है।

तो एक एनुम का अंतर्निहित प्रकार कार्यान्वयन-परिभाषित है, जिसमें एक मामूली प्रतिबंध है।

अगला हम "अपूर्ण प्रकार" (3.9) पर सेक्शन पर आते हैं, जो कि लगभग उतना ही है जितना कि हम आगे की घोषणा पर किसी भी मानक पर आते हैं:

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

एक वर्ग प्रकार (जैसे "कक्षा X") एक अनुवाद इकाई में एक बिंदु पर अधूरा हो सकता है और बाद में पूरा हो सकता है; प्रकार "वर्ग X" दोनों बिंदुओं पर समान प्रकार है। एक सरणी वस्तु का घोषित प्रकार अपूर्ण वर्ग प्रकार का एक सरणी हो सकता है और इसलिए अपूर्ण है; यदि अनुवाद इकाई में कक्षा प्रकार बाद में पूरा हो जाता है, तो सरणी प्रकार पूर्ण हो जाता है; उन दो बिंदुओं पर सरणी प्रकार एक ही प्रकार है। एक सरणी वस्तु का घोषित प्रकार अज्ञात आकार का एक सरणी हो सकता है और इसलिए अनुवाद इकाई में एक बिंदु पर अपूर्ण और बाद में पूरा हो सकता है; उन दो बिंदुओं पर सरणी प्रकार ("T की अज्ञात बाउंड की सरणी" और "N T की सरणी") विभिन्न प्रकार हैं। अज्ञात आकार के सरणी के लिए एक सूचक का प्रकार, या अज्ञात आकार के एक सरणी होने के लिए टाइप किए गए घोषणा द्वारा परिभाषित प्रकार का।

इसलिए, मानक ने बहुत अधिक उन प्रकारों को निर्धारित किया है जिन्हें आगे घोषित किया जा सकता है। एनम नहीं था, इसलिए संकलक लेखक आम तौर पर अपने अंतर्निहित प्रकार के चर आकार के कारण मानक द्वारा अस्वीकृत घोषित करने के बारे में सोचते हैं।

यह भी समझ में आता है। Enums आमतौर पर उप-मूल्य स्थितियों में संदर्भित होते हैं, और संकलक को वास्तव में उन स्थितियों में भंडारण आकार जानने की आवश्यकता होगी। चूंकि भंडारण आकार को लागू किया गया है, इसलिए कई कंपाइलर हर प्रकार के एन्यूमर के लिए 32 बिट मान का उपयोग करने का विकल्प चुन सकते हैं, जिस बिंदु पर उन्हें घोषित करना संभव हो जाता है। एक दिलचस्प प्रयोग दृश्य स्टूडियो में एक एनम की घोषणा करने की कोशिश करने के लिए हो सकता है, फिर इसे आकार (इंट) से अधिक अंतर्निहित प्रकार का उपयोग करने के लिए मजबूर करता है जैसा कि ऊपर बताया गया है कि क्या होता है।


ध्यान दें कि यह स्पष्ट रूप से "enum foo;" 7.1.5.3/1 में (लेकिन सब कुछ के साथ, जब तक कंपाइलर चेतावनी देता है, यह अभी भी इस तरह के कोड को संकलित कर सकता है, निश्चित रूप से)
जोहान्स स्काउब - लीब

इसे इंगित करने के लिए धन्यवाद, यह वास्तव में गूढ़ पैराग्राफ है और मुझे इसे पार्स करने में एक सप्ताह लग सकता है। लेकिन यह जानना अच्छा है कि यह वहां है।
दान ओल्सन

कोई चिंता नहीं। कुछ मानक पैराग्राफ वास्तव में अजीब हैं :) अच्छी तरह से, एक विस्तृत प्रकार विनिर्देशक कुछ ऐसा है जहां आप एक प्रकार निर्दिष्ट करते हैं, लेकिन इसे अस्पष्ट बनाने के लिए कुछ और भी निर्दिष्ट करते हैं। उदाहरण के लिए "X" के बजाय "स्ट्रक्चर X", या "Enum Y" के बजाय केवल "Y" है। आपको किसी चीज़ को मुखर करने के लिए इसकी आवश्यकता है।
जोहान्स शाउब -

तो आप इसे इस तरह से उपयोग कर सकते हैं: "दसवीं कक्षा * फू;" यदि X को आगे घोषित नहीं किया गया था। या "टाइपनेम X :: foo" असंतोष के लिए एक टेम्पलेट में। या "क्लास लिंक ओब्ज;" यदि समान स्कोप में कोई फ़ंक्शन "लिंक" होता है, जो समान नाम वाले वर्ग को छायांकित करेगा।
जोहान्स स्काउब -

3.4.4 में यह कहा गया है कि यदि कुछ गैर-प्रकार का नाम एक प्रकार का नाम छुपाता है तो उनका उपयोग किया जाता है। यही वह जगह है जहां वे अक्सर उपयोग किए जाते हैं, इसके अलावा "दसवीं कक्षा;" (यहाँ यह एक घोषणा का एकमात्र आधार है)। यह उनके बारे में यहां गैर-टेम्पलेट्स में बात करता है। हालाँकि, 14.6 / 3 टेम्प्लेट में उनके उपयोग को सूचीबद्ध करता है।
जोहान्स शाउब -

1

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

  1. निम्नलिखित कोड ठीक संकलित किया गया है।
    typedef int myint;
    एनम टी;
    शून्य फू (टी * टीपी)
    {
        * tp = (T) 0x12345678;
    }
    एनम टी: चार
    {
        ए
    };

लेकिन / W4 के लिए चेतावनी मिली (/ W3 इस चेतावनी को लाइक नहीं)

चेतावनी C4480: गैरमानक एक्सटेंशन का उपयोग किया गया: एनम 'टी' के लिए अंतर्निहित प्रकार निर्दिष्ट करना

  1. उपर्युक्त मामले में VC (Microsoft (R) 32-बिट C / C ++ ऑप्टिमाइज़िंग कंपाइलर संस्करण 15.00.30729.01 80x86 के लिए छोटी गाड़ी है):

    • जब देखकर एनम टी; वीसी मान लेता है कि एनम टाइप T डिफ़ॉल्ट 4 बाइट्स का उपयोग अंतर्निहित प्रकार के रूप में करता है, इसलिए उत्पन्न विधानसभा कोड है:
    ; foo @@ YAXPAW4T @@@ Z PROC; foo
    ; फ़ाइल e: \ work \ c_cpp \ cpp_snippet.cpp
    ; पंक्ति १३
        धक्कामुक्की
        मूव एबप, एस्प
    ; लाइन 14
        Mov eax, DWORD PTR _tp $ [ebp]
        Mov DWORD PTR [eax], 305419896; 12345678H
    ; पंक्ति 15
        पॉप ईबे
        रिटायर ०
    ; foo @@ YAXPAW4T @@@ Z ENDP; foo

उपरोक्त विधानसभा कोड को सीधे मेरे व्यक्तिगत अनुमान से नहीं, बल्कि /Fatest.asm से निकाला जाता है। क्या आप Mov DWORD PTR [eax], 305419896 देखते हैं; 12345678H लाइन?

निम्नलिखित कोड स्निपेट यह साबित करता है:

    int main (int argc, char * argv)
    {
        संघ {
            चार सीए [4];
            टी टी;
        }ए;
        a.ca [0] = a.ca [1] = a। [ca [2] = a.ca [3] = 1;
        foo (& a.t);
        प्रिंटफ ("% # x,% # x,% # x,% # x \ n", a.ca [0], a.ca [1], a.ca [2], a.ca [3]) ;
        वापसी 0;
    }

परिणाम है: 0x78, 0x56, 0x34, 0x12

  • एनम टी की आगे की घोषणा को हटा दें और एनुम टी की परिभाषा के बाद फ़ंक्शन फू की परिभाषा को स्थानांतरित करें: परिणाम ठीक है:

उपरोक्त प्रमुख अनुदेश बन जाता है:

Mov BYTE PTR [eax], 120; 00000078H

अंतिम परिणाम है: 0x78, 0x1, 0x1, 0x1

ध्यान दें कि मूल्य ओवरराइट नहीं किया जा रहा है

इसलिए वीसी में एनम की फॉरवर्ड-घोषणा का उपयोग करना हानिकारक माना जाता है।

BTW, आश्चर्य नहीं करने के लिए, अंतर्निहित प्रकार की घोषणा के लिए सिंटैक्स सी # में समान है। प्रैटिस में मैंने पाया कि अंतर्निहित सिस्टम से बात करते समय अंतर्निहित प्रकार को चार के रूप में निर्दिष्ट करके 3 बाइट को बचाने के लायक है, जो कि मेमोरी सीमित है।


1

अपनी परियोजनाओं में, मैंने विरासत और 3-पार्टी घटकों से निपटने के लिए नेमस्पेस-बाउंड एन्यूमरेशन तकनीक को अपनाया enum। यहाँ एक उदाहरण है:

forward.h:

namespace type
{
    class legacy_type;
    typedef const legacy_type& type;
}

enum.h:

// May be defined here or pulled in via #include.
namespace legacy
{
    enum evil { x , y, z };
}


namespace type
{
    using legacy::evil;

    class legacy_type
    {
    public:
        legacy_type(evil e)
            : e_(e)
        {}

        operator evil() const
        {
            return e_;
        }

    private:
        evil e_;
    };
}

foo.h:

#include "forward.h"

class foo
{
public:
    void f(type::type t);
};

foo.cc:

#include "foo.h"

#include <iostream>
#include "enum.h"

void foo::f(type::type t)
{
    switch (t)
    {
        case legacy::x:
            std::cout << "x" << std::endl;
            break;
        case legacy::y:
            std::cout << "y" << std::endl;
            break;
        case legacy::z:
            std::cout << "z" << std::endl;
            break;
        default:
            std::cout << "default" << std::endl;
    }
}

main.cc:

#include "foo.h"
#include "enum.h"

int main()
{
    foo fu;
    fu.f(legacy::x);

    return 0;
}

ध्यान दें कि foo.hहेडर के बारे में कुछ भी जानना जरूरी नहीं है legacy::evil। केवल वे फ़ाइलें जो विरासत प्रकार का उपयोग करती हैं legacy::evil(यहाँ: main.cc) को शामिल करने की आवश्यकता है enum.h


0

आपकी समस्या का मेरा समाधान या तो होगा:

1 - एनम के बजाय int का उपयोग करें: अपनी CPP फ़ाइल में एक अनाम नेमस्पेस में अपने इनर्ट्स को डिक्लेयर करें (हेडर में नहीं):

namespace
{
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
}

जैसा कि आपके तरीके निजी हैं, कोई भी डेटा के साथ गड़बड़ नहीं करेगा। यदि कोई आपको अमान्य डेटा भेजता है, तो आप परीक्षण करने के लिए और भी आगे बढ़ सकते हैं:

namespace
{
   const int FUNCTIONALITY_begin = 0 ;
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
   const int FUNCTIONALITY_end = 3 ;

   bool isFunctionalityCorrect(int i)
   {
      return (i >= FUNCTIONALITY_begin) && (i < FUNCTIONALITY_end) ;
   }
}

2: जावा में किए गए सीमित कॉन्स्टेंट इंस्टेंशन्स के साथ एक पूर्ण कक्षा बनाएं। फ़ॉरवर्ड क्लास घोषित करें, और फिर इसे CPP फ़ाइल में परिभाषित करें, और केवल एनुम-जैसे मानों को इंस्टेंटनेट करें। मैंने सी ++ में ऐसा कुछ किया था, और परिणाम वांछित के रूप में संतोषजनक नहीं था, क्योंकि यह एक एनम (कॉपी निर्माण, ऑपरेटर =, आदि) को अनुकरण करने के लिए कुछ कोड की आवश्यकता थी।

3: जैसा कि पहले प्रस्तावित था, निजी तौर पर घोषित एनम का उपयोग करें। इस तथ्य के बावजूद कि कोई उपयोगकर्ता इसकी पूरी परिभाषा देखेगा, वह इसका उपयोग नहीं कर सकेगा, न ही निजी तरीकों का उपयोग कर सकेगा। तो आप आम तौर पर अपनी कक्षा का उपयोग करके कोड को फिर से जमा करने की आवश्यकता के बिना मौजूदा तरीकों और सामग्री को संशोधित करने में सक्षम होंगे।

मेरा अनुमान या तो समाधान 3 या 1 होगा।


-1

क्योंकि एनम अलग-अलग आकार का एक अभिन्न आकार हो सकता है (कंपाइलर यह तय करता है कि किसी दिए गए एनम का आकार क्या है), एनम के लिए पॉइंटर का भी अलग-अलग आकार हो सकता है, क्योंकि यह एक अभिन्न प्रकार है (चार्ट में कुछ प्लेटफार्मों पर एक अलग आकार के संकेत हैं) उदाहरण के लिए)।

इसलिए कंपाइलर आपको एनम और फ़ॉरवर्ड करने का ऐलान भी नहीं कर सकता है और उपयोगकर्ता को इसका पॉइंटर भी दे सकता है, क्योंकि वहां भी उसे एनम के आकार की आवश्यकता होती है।


-1

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

जब आप इस तथ्य की घोषणा करते हैं कि आप 'सीमित सेट' का उपयोग करेंगे, तो बाद में कोई मूल्य नहीं जोड़ा जाएगा: बाद के कोड को इससे लाभ प्राप्त करने के लिए संभावित मूल्यों को जानना होगा।

हालांकि संकलक है enumerated प्रकार के आकार के बारे में चिंतित, आशय गणन के खो दिया हो जाता है जब आप आगे यह घोषणा करते हैं।


1
नहीं, बाद के कोड को इसके उपयोगी होने के लिए मूल्यों की आवश्यकता नहीं है - विशेष रूप से, यदि बाद का कोड केवल एक फ़ंक्शन प्रोटोटाइप है जो एनम मापदंडों को ले या वापस कर रहा है, तो प्रकार का आकार महत्वपूर्ण नहीं है। आगे की घोषणा का उपयोग कर निर्माण निर्भरता को दूर कर सकते हैं, तेजी से संकलन।
j_random_hacker

आप सही हे। आशय मूल्यों को मानने का नहीं, बल्कि प्रकार का है। 0x Enum प्रकारों के साथ हल किया गया।
xtofl
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.