2013-02-11 7 views
7

std::vector जैसे कंटेनर वर्ग के साथ, निरंतर-नस्ल की दो अलग-अलग अवधारणाएं हैं: कंटेनर (यानी इसका आकार) और तत्वों का। ऐसा लगता है कि इन दो std::vector, confuses ऐसी है कि निम्नलिखित सरल कोड संकलन नहीं होगा:कंटेनर का बनाम गैर-कॉन्स और इसकी सामग्री

struct A { 
    A(size_t n) : X(n) {} 
    int&x(int i) const { return X[i]; } // error: X[i] is non-const. 
private: 
    std::vector<int> X; 
}; 

नोट है कि भले ही डेटा सदस्यों को (तीन संकेत दिए गए डेटा और अंत की & अंत शुरू आवंटित बफर का) std::vectoroperator[] पर कॉल द्वारा बदला गया, यह सदस्य const नहीं है - क्या यह एक अजीब डिज़ाइन नहीं है?

भी ध्यान रखें कि कच्चे संकेत के लिए, निरंतर सत्ता के इन दो अवधारणाओं बड़े करीने से अलग होती है, जैसे कि इसी कच्चे सूचक कोड

struct B { 
    B(size_t n) : X(new int[n]) {} 
    ~B() { delete[] X; } 
    void resize(size_t n);     // non-const 
    int&x(int i) const { return X[i]; } // fine 
private: 
    int*X; 
}; 

काम करता है ठीक।

तो std::vector (mutable का उपयोग किए बिना) के साथ निपटने के लिए सही/अनुशंसित तरीका क्या है?

(, इसलिए कोई यूबी यहाँ X गैर const माना जाता है) एक const_cast<> रूप

int&A::x(int i) const { return const_cast<std::vector<int>&>(X)[i]; } 

में स्वीकार्य समझा है?

संपादित बस आगे भ्रम को रोकने के लिए: मैं तत्वों, कंटेनर की अर्थात सामग्री लेकिन संशोधित करना चाहते हैं नहीं कंटेनर ही (आकार और/या स्मृति स्थान)।

+0

'std :: vector' का डेटा आपके कॉल द्वारा 'ऑपरेटर []' में बदला जा सकता है। जैसा कि आपने 'ए :: एक्स',' ए.एक्स (1) ++ लिखा है, 'पूरी तरह से कानूनी है, और वेक्टर की सामग्री को संशोधित करता है। –

+1

@ डेविडस्वार्टज़ * वेक्टर का * सामग्री * वास्तविक * डेटा * नहीं है (हालांकि आप उन्हें तर्कसंगत रूप से इस तरह से जोड़ सकते हैं)। यदि आप 'std :: vector' का निरीक्षण करते हैं, तो इसमें डेटा के रूप में केवल 3 पॉइंटर्स हैं (डेटा का प्रारंभ और अंत और बफर का अंत)। ये अपरिवर्तित रहते हैं। – Walter

उत्तर

13

सी ++ केवल const के एक स्तर का समर्थन करता है। जहां तक ​​संकलक का संबंध है, यह बिटवाइज़ स्थिरांक है: "बिट्स" वास्तव में वस्तु (यानी sizeof में गिना जाता है) गेम खेलने (const_cast, आदि) के बिना संशोधित नहीं किया जा सकता है, लेकिन कुछ और उचित है खेल । सी ++ (1 9 80 के दशक के उत्तरार्ध में, 1 99 0 के दशक के शुरुआती दिनों में) बिटवाई कॉन्स बनाम लॉजिकल कॉन्स (जिसे हम्प्टी-डम्प्टी कॉन्स, के नाम से भी जाना जाता है) के डिज़ाइन फायदों के बारे में बहुत सी चर्चा थी क्योंकि एंडी कोएनिग ने मुझे एक बार कहा था , जब प्रोग्रामर const का उपयोग करता है, तो इसका अर्थ यह है कि प्रोग्रामर इसका मतलब क्या चाहता है)। सर्वसम्मति से तार्किक आधार के पक्ष में आम सहमति मिली।

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

आप की तरह मैं कई बार सामना किया है जब मैं मना करना चाहता था, (अगर एक सी शैली सरणी स्थिरांक है, तो आप उसके तत्वों के किसी भी संशोधित नहीं कर सकते शायद सी शैली सरणियों के साथ समानांतर यहाँ एक भूमिका निभाई।) वेक्टर के आकार का संशोधन (शायद इटरेटर्स की रक्षा के लिए), लेकिन इसके तत्वों का नहीं। वास्तव में संतोषजनक समाधान नहीं हैं; सबसे अच्छा मैं सोच सकता हूं कि एक नया प्रकार है, जिसमें mutable std::vector है, और फ़ॉरवर्डिंग फ़ंक्शंस प्रदान करें जो const के अर्थ से मेल खाते हैं, मुझे इस विशिष्ट मामले में आवश्यकता है। और यदि आप तीन स्तरों (पूरी तरह से कॉन्स्ट, आंशिक रूप से कॉन्स्ट, और गैर-कॉन्स) को अलग करना चाहते हैं, आपको व्युत्पन्न की आवश्यकता होगी। बेस क्लास केवल पूरी तरह से कॉन्स्ट और आंशिक रूप से कॉन्स फ़ंक्शन का खुलासा करता है (उदा। const int operator[](size_t index) const; और int operator[]( size_t index);, लेकिन void push_back(int); नहीं); फ़ंक्शंस जो किसी तत्व के सम्मिलन और निष्कासन की अनुमति देता है केवल व्युत्पन्न कक्षा में उजागर होता है। जिन ग्राहकों को तत्वों को सम्मिलित या निकालना नहीं चाहिए, वे बेस क्लास में केवल गैर-कॉन्स्ट संदर्भ पास कर चुके हैं।

+0

+1 अच्छा नहीं होगा चर्चा। मुझे विश्वास नहीं है, हालांकि, इस तथ्य के बारे में कि एक कंटेनर वर्ग के डिजाइनर के पास कोई विकल्प नहीं है। कोई एक कंटेनर क्लास को कार्यान्वित कर सकता है जो उसके तत्वों के कॉन्स्टेस को टेम्पलेट तर्क के रूप में रखता है और कॉन्स्ट और गैर-कॉन्स कंटेनर के बीच चाल-रूपांतरण (स्मार्ट पॉइंटर्स का उपयोग करके) की अनुमति देता है। आपका बेस-व्युत्पन्न डिज़ाइन सरल दिखता है, हालांकि। – Walter

+0

@ वाल्टर एक कंटेनर वर्ग के डिजाइनर के पास सभी प्रकार के विकल्प हैं। 'Std :: vector' के डिजाइनर ने इस मामले में, एक सामान्य आम सहमति प्रतीत होती है --- अधिकांश पूर्व-मानक कंटेनर मुझे ऐसा ही पता है। वैश्विक स्तर पर, एक कंटेनर की एक बड़ी मांग प्रतीत नहीं होती है जो विशेष मामलों (उदा। 'Std :: array') को छोड़कर, 'const' (कोई भी, आंशिक या पूर्ण) के तीन स्तर प्रदान नहीं करता है। (पूर्व-मानक दिनों में, मेरी सरणी कक्षा में से एक ने कन्स्ट्रक्टर तर्क के रूप में आकार लिया, और बाद में इसे बदलने की कोई संभावना नहीं दी।) –

+0

@ जेम्ससांज: स्पष्टीकरण उत्कृष्ट है, हालांकि शीर्ष पर पहले दो उद्घाटन वाक्य दिए गए इंप्रेशन कि बिटवाईड कॉन्स वह है जो सी ++ का समर्थन करता है। * (मुझे उम्मीद है कि बहुत सारे सी ++ प्रोग्रामर नहीं हैं जो टीएल हैं; डीआर।) * यह उद्घाटन वाक्य कंपाइलर बैक एंड पर सच है, लेकिन फ्रंट एंड केवल 'कॉन्स्टैक्टिक सहायता' के रूप में 'कॉन्स्ट' मानता है - यह है प्रकार की जांच में प्रयोग किया जाता है, यही वह है। (वैसे, सी ++ 11 संकलन-समय 'constexpr' प्रस्तुत करता है।) लॉजिकल कॉन्स्ट-नेस केवल प्रोग्रामर के दिमाग में मौजूद है। जब कोई बहु-थ्रेडेड प्रोग्रामिंग शुरू करता है तो एक सी ++ प्रोग्रामर अंतर को महसूस करेगा। – rwong

3

यह एक अजीब डिजाइन नहीं है, यह एक बहुत ही जानबूझकर विकल्प है, और सही एक IMHO है।

आपका B उदाहरण एक std::vector के लिए एक अच्छा सादृश्य नहीं है, एक बेहतर सादृश्य होगा:

struct C { 
    int& get(int i) const { return X[i]; } 
    int X[N]; 
}; 

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

मैं कहूंगा कि const_cast स्वीकार्य नहीं है, और न तो अंतिम उपाय के रूप में mutable का उपयोग कर रहा है। आपको पूछना चाहिए कि आप एक कॉन्स ऑब्जेक्ट का डेटा क्यों बदलना चाहते हैं, और सदस्य को गैर-कॉन्स्टेंस बनाने पर विचार करें।

+0

यह व्याख्या का विषय है। यदि आप एक 'वेक्टर' को सी-सरणी के रूप में देखते हैं जो पुनः आकार देने योग्य है, तो मैं सहमत हूं। हालांकि, फिर इस आकार के साथ 'आकार बदलें() 'आदि कार्य कहां फिट होते हैं? वे निरंतर से अधिक होना चाहिए: आप तत्वों के लिए गैर-कॉन्स पहुंच प्रदान करने में सक्षम होना चाहते हैं लेकिन आकार के लिए नहीं। यह 'std :: vector' के साथ संभव नहीं है। मुझे लगता है कि हल करने का सामान्य तरीका यह है कि इसे इटरेटर प्रदान करना है, जो आम तौर पर सामग्री को बदलने की अनुमति देता है, लेकिन कंटेनर नहीं। – Walter

+0

यह सिर्फ एक समानता है, इसे शाब्दिक रूप से न लें, वैसे भी 'आकार बदलें' नहीं है, इसलिए इसे ऑब्जेक्ट को म्यूट करने की अनुमति है। यह सुनिश्चित नहीं है कि आप इटरेटर के बारे में क्या मतलब रखते हैं, लेकिन मानक कंटेनर में से कोई भी आपको एक कॉन्स कंटेनर के लिए एक गैर-कॉन्स्टेटर इटेटरेटर देगा। –

+0

इटेटरेटर के बारे में मेरा क्या मतलब है कि यदि आपके पास एक (गैर-कॉन्स) इटरेटर है तो आप कंटेनर को संशोधित नहीं कर सकते हैं। इसलिए कंटेनर स्थिर रहता है जबकि तत्वों को संशोधित किया जा सकता है। – Walter

4

दुर्भाग्य से, संकेत के विपरीत, आप की तरह

std::vector<int> i; 
std::vector<const int>& ref = i; 

कुछ क्यों std::vectorconst के दो प्रकार को स्पष्ट करने में नहीं कर सकते हैं के रूप में वे लागू हो सकता है कि ऐसा नहीं कर सकते हैं, और यह रूढ़िवादी हो गया है।मैं व्यक्तिगत रूप से की तरह

const_cast<int&>(X[i]); 

संपादित कुछ करने के लिए चुनते हैं: जैसा कि एक और टिप्पणीकार इसे सही बताया, iterators मॉडल इस विरोधाभास है। यदि आपने शुरुआत में vector<int>::iterator संग्रहीत किया है, तो आप इसे किसी कॉन्स्ट विधि में संदर्भित कर सकते हैं और गैर-कॉन्स int& वापस प्राप्त कर सकते हैं। मुझे लगता है। लेकिन आपको अमान्यता से सावधान रहना होगा।

+1

इटरेटर्स का उपयोग एक दिलचस्प समाधान सुझाता है: एक _view_ कक्षा जिसमें केवल प्रारंभ और अंत इटरेटर होता है, और वह केवल सीमित कार्यक्षमता प्रदान करता है जो वह चाहता है। (उस मामले के लिए, वेक्टर के सूचक के साथ एक व्यू क्लास भी करेगा।) –

-1

मैं के बजाय std::vector::at() विधि का उपयोग करने का सुझाव दूंगा।

+0

'वेक्टर :: पर() 'का कॉन्स अधिभार एक' const 'संदर्भ देता है, जिससे –

संबंधित मुद्दे