वेक्टर <bool> एक STL कंटेनर क्यों नहीं है?


99

स्कॉट मेयर्स की किताब का आइटम 18 प्रभावी एसटीएल: मानक टेम्पलेट लाइब्रेरी के आपके उपयोग को बेहतर बनाने के 50 विशिष्ट तरीके कहते हैं कि इससे बचने के लिए vector <bool>एसटीएल कंटेनर नहीं है और यह वास्तव में पकड़ में नहीं आता है bool

निम्नलिखित कोड:

vector <bool> v; 
bool *pb =&v[0];

संकलित नहीं करेगा, एसटीएल कंटेनरों की आवश्यकता का उल्लंघन करते हुए।

त्रुटि:

cannot convert 'std::vector<bool>::reference* {aka std::_Bit_reference*}' to 'bool*' in initialization

vector<T>::operator []वापसी प्रकार माना जाता है T&, लेकिन यह एक विशेष मामला क्यों है vector<bool>?

vector<bool>वास्तव में क्या होता है?

आइटम आगे कहता है:

deque<bool> v; // is a STL container and it really contains bools

क्या इसे एक विकल्प के रूप में इस्तेमाल किया जा सकता है vector<bool>?

क्या कोई इसे समझा सकता है?


22
यह C ++ 98 में डिज़ाइन की गलती थी, जिसे अब संगतता के लिए बरकरार रखा गया है।
ओकटालिस्ट

8
@ g-makulik, ऐसा नहीं है कि इसका उपयोग संकलित नहीं होगा, बस आप किसी तत्व के पते को पॉइंटर में स्टोर नहीं कर सकते bool, क्योंकि तत्व का अपना पता नहीं है।
क्रिस

2
शायद यह मदद मिलेगी: stackoverflow.com/questions/670308/alternative-to-vectorbool
क्रिस

1
@ जी-मल्लिक std::vector<bool> v;का संकलन होगा। &v[0]नहीं होगा (अस्थायी का पता नहीं)।
ओकटालिस्ट

4
vector<bool>एक बुरा प्रतिनिधि है, लेकिन पूरी तरह से उचित नहीं है: isocpp.org/blog/2012/11/on-vectorbool
TemplateRex

जवाबों:


114

अंतरिक्ष-अनुकूलन कारणों के लिए, C ++ मानक (जहाँ तक C ++ 98) स्पष्ट vector<bool>रूप से एक विशेष मानक कंटेनर के रूप में पुकारता है, जहाँ प्रत्येक बूल एक बाइट के बजाय केवल एक बिट स्थान का उपयोग करता है, एक सामान्य बूल के रूप में (एक प्रकार का कार्यान्वयन) "गतिशील बिटसेट")। इस अनुकूलन के बदले में यह सामान्य मानक कंटेनर की सभी क्षमताओं और इंटरफ़ेस की पेशकश नहीं करता है।

इस मामले में, चूंकि आप एक बाइट के भीतर एक बिट का पता नहीं ले सकते हैं, जैसे कि चीजें operator[]वापस नहीं आ सकती हैं, bool&लेकिन इसके बजाय एक प्रॉक्सी ऑब्जेक्ट लौटाते हैं जो विशेष बिट को प्रश्न में हेरफेर करने की अनुमति देता है। चूँकि यह प्रॉक्सी ऑब्जेक्ट नहीं है bool&, इसलिए आप इसके पते को ऐसे नहीं दे सकते हैं bool*जैसे आप "सामान्य" कंटेनर पर ऐसे ऑपरेटर कॉल के परिणाम के साथ कर सकते हैं। बदले में इसका मतलब है कि bool *pb =&v[0];मान्य कोड नहीं है।

दूसरी ओर dequeऐसी कोई विशेषज्ञता नहीं है, जिसे कहा जाता है कि प्रत्येक बाइट एक बाइट लेता है और आप मूल्य वापसी का पता ले सकते हैं operator[]

अंत में ध्यान दें कि एमएस मानक पुस्तकालय कार्यान्वयन (यकीनन) उप-रूप में है कि यह देवताओं के लिए एक छोटा हिस्सा आकार का उपयोग करता है, जिसका अर्थ है कि विकल्प के रूप में deque का उपयोग करना हमेशा सही उत्तर नहीं होता है।


5
क्या हमारे पास कोई अन्य डेटा प्रकार है जिसके लिए कोई अन्य एसटीएल कंटेनर विशिष्ट या स्पष्ट रूप से कहा जाता है?
P0W

3
क्या यह C ++ 11 std :: array <bool> के लिए लागू होता है?
सर्जियो बसुरको

4
@chuckleplant no, std::arrayकच्चे T[n]सहायक के चारों ओर केवल एक टेम्प्लेटेड रैपर है, जिसमें कुछ सहायक कार्य जैसे size()कॉपी / मूवमेंट शब्दार्थ है, और पुनरावृत्तियाँ इसे STL- संगत बनाने के लिए जोड़े गए हैं - और (शुक्र है) यह अपने स्वयं के सिद्धांतों का उल्लंघन नहीं करता है (मेरा नोट इन पर संदेह :) '' के लिए 'विशेषज्ञ bool'।
अंडरस्कोर_ड

बस एक नाइट पिक - आकार (बूल) जरूरी नहीं कि एक बाइट हो। stackoverflow.com/questions/4897844/…
उड़ी राज

30

vector<bool>मूल्य के लिए केवल एक बिट का उपयोग करके संपीड़ित रूप में बूलियन मान होता है (और न कि 8 कैसे बूल [] सरणियाँ)। सी ++ में किसी संदर्भ को वापस करना संभव नहीं है, इसलिए एक विशेष सहायक प्रकार है, "बिट संदर्भ", जो आपको स्मृति में कुछ बिट के लिए एक इंटरफ़ेस प्रदान करता है और आपको मानक ऑपरेटर और कास्ट का उपयोग करने की अनुमति देता है।


1
@PrashantSrivastava deque<bool>विशेष नहीं है, इसलिए इसका शाब्दिक अर्थ है केवल एक छलावा धारण करना।
कोनराड रुडोल्फ

@PrashantSrivastava vector<bool>का एक विशिष्ट टेम्पलेट कार्यान्वयन है। मुझे लगता है, अन्य एसटीएल कंटेनर, जैसे deque<bool>, नहीं, इसलिए वे किसी अन्य प्रकार की तरह बूल-एस रखते हैं।
इवान स्मिरनोव

यहाँ एक सवाल जंग में एक ऐसी ही बात है, जहां वे एक बिट बूलियन्स अनुमति नहीं पूछ रहा है stackoverflow.com/questions/48875251/...
बूट एंडी

25

समस्या यह है कि एक सच्चे संदर्भ के बजाय vector<bool>एक प्रॉक्सी संदर्भ ऑब्जेक्ट लौटाता है , ताकि C ++ 98 शैली कोड bool * p = &v[0];संकलित न हो। हालाँकि, आधुनिक C ++ 11 auto p = &v[0];को संकलित करने के लिए बनाया जा सकता है यदि वह प्रॉक्सी पॉइंटर ऑब्जेक्ट कोoperator& भी लौटाता है । हावर्ड हिन्नेंट ने इस तरह के प्रॉक्सी संदर्भों और बिंदुओं का उपयोग करते हुए एल्गोरिथम सुधार का विवरण देते हुए एक ब्लॉग पोस्ट लिखा है

स्कॉट मेयर्स में प्रॉक्सी क्लासेस के बारे में अधिक प्रभावी C ++ में एक लंबी आइटम 30 है । आप बिल्टिन प्रकारों की लगभग नकल करने के लिए एक लंबा रास्ता तय कर सकते हैं : किसी भी प्रकार के लिए T, प्रॉक्सी (जैसे reference_proxy<T>और iterator_proxy<T>) की एक जोड़ी को इस अर्थ में पारस्परिक रूप से सुसंगत बनाया जा सकता है कि reference_proxy<T>::operator&()और iterator_proxy<T>::operator*()एक दूसरे के व्युत्क्रम हैं।

हालाँकि, कुछ बिंदु पर किसी को छद्म वस्तुओं को मैप करने की आवश्यकता होती है जैसे T*या व्यवहार करने के लिए T&। इटरेटर प्रॉक्सिस के लिए, operator->()सभी टेम्प्लेट Tको पुन: कार्यान्वित किए बिना टेम्प्लेट के इंटरफ़ेस को ओवरलोड और एक्सेस कर सकता है। हालाँकि, संदर्भ के संदर्भ के लिए, आपको ओवरलोड करने की आवश्यकता होगी operator.(), और वर्तमान सी ++ में इसकी अनुमति नहीं है (हालांकि सेबस्टियन रेडल ने BoostCon 2013 में ऐसा प्रस्ताव प्रस्तुत किया था)। आप .get()संदर्भ प्रॉक्सी के अंदर सदस्य की तरह एक क्रिया-कार्य कर सकते हैं , या संदर्भ के अंदर के सभी Tइंटरफ़ेस को लागू कर सकते हैं (यह वही है जो इसके लिए किया जाता हैvector<bool>::bit_reference), लेकिन यह या तो बिल्ट सिंटैक्स को खो देगा या उपयोगकर्ता-परिभाषित रूपांतरणों को पेश करेगा, जिसमें टाइप रूपांतरणों के लिए बिलिन शब्दार्थ नहीं है (आप प्रति तर्क में सबसे अधिक उपयोगकर्ता-परिभाषित रूपांतरण हो सकते हैं)।

टीएल; डीआर : कोई vector<bool>कंटेनर नहीं है क्योंकि मानक को एक वास्तविक संदर्भ की आवश्यकता होती है, लेकिन यह लगभग कंटेनर की तरह व्यवहार करने के लिए बनाया जा सकता है, C ++ 98 की तुलना में C ++ 11 (ऑटो) के साथ कम से कम बहुत करीब है।


10

कई लोग vector<bool>विशेषज्ञता को गलती मानते हैं ।

एक पेपर में "C ++ 17 में वेस्टीजियल लाइब्रेरी पार्ट्स डिप्रेसिंग" में पुन: विचार करने वाले वेक्टर आंशिक विशेषज्ञता का
प्रस्ताव है ।

एसटीडी के बूल आंशिक विशेषज्ञता का एक लंबा इतिहास रहा है :: वेक्टर कंटेनर आवश्यकताओं को संतुष्ट नहीं करता है, और विशेष रूप से, इसके पुनरावृत्तियों ने यादृच्छिक अभिगम पुनरावृत्ति की आवश्यकताओं को पूरा नहीं किया है। इस कंटेनर को चित्रित करने का पिछला प्रयास C ++ 11, N2204 के लिए अस्वीकार कर दिया गया था ।


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


5

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

उदाहरण के लिए stdc ++ कार्यान्वयन को यहाँ देखें

भी भले ही दिलचस्प नहीं एक STL बिट वेक्टर अनुरूप है LLVM :: BitVector से यहाँ

का सार llvm::BitVectorएक नेस्टेड वर्ग है जिसे कुछ सीमाओं के साथ व्यवहार के समान referenceबनाने के लिए उपयुक्त ऑपरेटर को ओवरलोडिंग कहा जाता है । नीचे दिए गए कोड को दिखाने के लिए एक सरलीकृत इंटरफ़ेस है कि कैसे बिटवेक्टर एक वर्ग को छुपाता है जिसे वास्तविक कार्यान्वयन बनाने के लिए बुलाया जाता है, प्रत्येक मूल्य के लिए 1 बाइट का उपयोग किए बिना लगभग एक वास्तविक बूल की तरह व्यवहार करता है।BitVectorvectorreference

class BitVector {
public:
  class reference {
    reference &operator=(reference t);
    reference& operator=(bool t);
    operator bool() const;
  };
  reference operator[](unsigned Idx);
  bool operator[](unsigned Idx) const;      
};

यहाँ इस कोड में अच्छे गुण हैं:

BitVector b(10, false); // size 10, default false
BitVector::reference &x = b[5]; // that's what really happens
bool y = b[5]; // implicitly converted to bool 
assert(b[5] == false); // converted to bool
assert(b[6] == b[7]); // bool operator==(const reference &, const reference &);
b[5] = true; // assignment on reference
assert(b[5] == true); // and actually it does work.

इस कोड में वास्तव में दोष है, चलाने की कोशिश करें:

std::for_each(&b[5], &b[6], some_func); // address of reference not an iterator

काम नहीं करेगा क्योंकि assert( (&b[5] - &b[3]) == (5 - 3) );विफल हो जाएगा (भीतर llvm::BitVector)

यह बहुत ही सरल llvm संस्करण है। std::vector<bool>इसमें काम करने वाले पुनरावृत्तियाँ भी हैं। इस प्रकार कॉल for(auto i = b.begin(), e = b.end(); i != e; ++i)काम करेगा। और भी std::vector<bool>::const_iterator

हालाँकि इसमें अभी भी कुछ सीमाएँ हैं std::vector<bool>जो कुछ मामलों में अलग व्यवहार करती हैं।


3

यह http://www.cplusplus.com/reference/vector/vector-bool/ से आता है

बूल का वेक्टर यह वेक्टर का एक विशेष संस्करण है, जो टाइप बूल के तत्वों के लिए उपयोग किया जाता है और अंतरिक्ष के लिए अनुकूलन करता है।

यह निम्नलिखित परिवर्तनों के साथ वेक्टर के विशिष्ट संस्करण की तरह व्यवहार करता है:

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

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

बिटसेट एक ऐसा वर्ग है जो बिट्स के निश्चित आकार के सरणियों के लिए एक समान कार्यक्षमता प्रदान करता है।


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