इटरेटर के लिए कास्ट रैपर
संपादित करें : मुझे यह क्यों चाहिए, इसके लिए स्पष्टीकरण जोड़ा गया है और मैंने अभी तक कोई उत्तर नहीं होने के कारण कोड को अपडेट किया है
मेरे पास एक C ++ 11 सरणी-जैसी कक्षा है जो (रैंडम-एक्सेस-इटरेटर) के आसपास एक रैपर हो सकती है। अनुक्रमणिका-आधारित पहुंच और .begin()/ .end()बस पुनरावृत्ति के माध्यम से गुजर सकते हैं, लेकिन जब वस्तु होती है तो थोड़ी जटिलता होती है const:
template<class Size, typename DataIterator>
class Storage : public Size {
DataIterator iterator;
public:
Storage(const DataIterator &iterator, const Size &size) : Size(size), iterator(iterator) {}
auto operator[](index_t i)
-> decltype(iterator[i]) {
return iterator[i];
}
auto operator[](index_t i) const
-> MakeConst<decltype(iterator[i])> {
return iterator[i];
}
DataIterator begin() {return iterator;}
ConstWrapper<DataIterator> begin() const {return iterator;}
DataIterator end() {return iterator + this->size();}
ConstWrapper<DataIterator> end() const {return iterator + this->size();}
};
यदि हम केवल और DataIteratorके constसंस्करण से वापस आ गए हैं , तो लोग उस पुनरावृति के माध्यम से सरणी को गलत तरीके से संशोधित कर सकते हैं (गलत तरीके से!)।.begin().end()Storage const &
कंटेनर की तरह std::vectorदो अलग-अलग पुनरावृत्तियाँ ( ::iteratorऔर ::const_iterator) हैं, लेकिन हमारी Storageकक्षा में केवल पढ़ने-लिखने वाला एक है, इसलिए हम एक का संश्लेषण करते हैं ConstWrapper:
template <typename Iterator>
class ConstWrapper {
Iterator iterator;
using traits = std::iterator_traits<Iterator>;
public:
using difference_type = typename traits::difference_type;
using value_type = typename traits::value_type;
using pointer = ConstWrapper;
using reference = MakeConst<typename traits::reference>;
using iterator_category = typename traits::iterator_category;
ConstWrapper() {}
ConstWrapper(const Iterator &iterator) : iterator(iterator) {}
// The problematic cases:
auto operator[](index_t i) const
-> MakeConst<decltype(iterator[i])> {
return iterator[i];
}
auto operator*() const
-> MakeConst<decltype(*iterator)> {
return *iterator;
}
bool operator!= (const ConstWrapper& other) const {
return iterator != other.iterator;
}
/** All the other random-access iterator methods **/
};
// Specialisation to prevent infinite loops
template <typename Iterator>
class ConstWrapper<ConstWrapper<Iterator>> : ConstWrapper<Iterator> {
public:
using ConstWrapper<Iterator>::ConstWrapper;
};
कार्यान्वयन सभी प्रासंगिक तरीकों को आगे बढ़ाता है, और इसके लिए एक विशेषज्ञता है ConstWrapper<ConstWrapper<...>>ताकि यह खुद को लपेट न सके।
एक महत्वपूर्ण हिस्सा है MakeConst, जो सही प्रकार में बदलता है const(केवल जोड़ने के विपरीत const, जिसका संदर्भों पर कोई प्रभाव नहीं है):
// Converts (T & -> T const &), and (T -> const T)
using MakeConst = typename std::conditional<
std::is_reference<T>::value,
typename std::remove_reference<T>::type const &,
const T
>::type;
क्या यह समझ में आता है, और क्या यह ठीक है? क्या कुछ और है जो मैं कर सकता हूं जो अधिक पठनीय / कुशल / आदि है।
धन्यवाद!
जवाब
मुझे नहीं लगता कि आपकी विशेषज्ञता वह कर रही है जो आपको लगता है कि यह कर रही है। https://godbolt.org/z/Gfh7sv
आप ConstWrapper<ConstWrapper<Iterator>>से विरासत में नहीं चाहते हैं ConstWrapper<Iterator>; इसका मतलब यह होगा कि एक CW<CW<I>>"एक तरह का है" CW<I>, जो सच नहीं है। आप यहाँ एक वंशानुक्रम संबंध नहीं चाहते हैं। आप जो चाहते हैं, वह केवल एस लपेटने की विशेषज्ञता के Storageलिए नहीं है DataIterator जो कि ConstWrapperपहले से ही विशेषज्ञता हो । जिस तरह से आप करते हैं वह अन्य प्रकार के उपनाम की अप्रत्यक्ष परत के साथ है:
template<class T> struct maybe_constwrap { using type = ConstWrapper<T>; };
template<class U> struct maybe_constwrap<ConstWrapper<U>> { using type = ConstWrapper<U>; };
template<class Size, typename DataIterator>
class Storage : public Size {
public:
using iterator = DataIterator;
using const_iterator = typename maybe_constwrap<DataIterator>::type;
~~~
};
अब, अगर DataIteratorपहले से ही एक है ConstWrapper, तो दोनों iteratorऔर const_iteratorसचमुच एक ही प्रकार होगा ... जो कि आप चाहते हैं।
आपके MakeConstविचार को संदिग्ध विचार पर समर्पित किया जा सकता है जो कि const _Bit_referenceअसाइन करने योग्य नहीं है। यह C ++ 23 में बदलने की संभावना है। देखhttps://stackoverflow.com/questions/63412623/should-c20-stdrangessort-not-need-to-support-stdvectorbool कुछ संदर्भ के लिए।
आप वापसी प्रकार में कुछ मेटाप्रोग्रामिंग को दोहराते हैं operator*और operator[]। उन्हें केवल उस प्रकार को वापस करना चाहिए referenceजो आपने पहले ही ऊपर गणना की है।
reference operator*() const { return *it_; }
संयोग से, मैं से अपने डेटा को सदस्य के नाम बदल रहा हूँ iteratorकरने के लिए it_है, क्योंकि मुझे लगता है कि यह एक भयानक विचार है, अवधारणाओं के लिहाज से, नाम के एक सदस्य के साथ एक कंटेनर-ish वर्ग प्रकार के लिए iteratorसदस्य है जो (ए) सार्वजनिक नहीं है, और (बी ) एक प्रकार नहीं।
आपका operator!=शायद छिपा दोस्त मुहावरा इस्तेमाल करना चाहिए:
friend bool operator==(ConstWrapper a, ConstWrapper b) { return a.it_ == b.it_; }
friend bool operator!=(ConstWrapper a, ConstWrapper b) { return a.it_ != b.it_; }
C ++ 20 में आप सैद्धांतिक रूप से चूक कर सकते हैं operator!=और operator==केवल प्रदान कर सकते हैं । मैं अभी तक नहीं जानता कि अगर यह एक अच्छा विचार है।
ConstWrapperमूल्य द्वारा पास करना ठीक होना चाहिए, क्योंकि यह केवल एक पुनरावृत्ति रखता है, और पुनरावृत्तियों को कॉपी करने के लिए सस्ता है।