एसटीएल-शैली के पुनरावृत्ति को कैसे लागू किया जाए और आम नुकसान से बचा जाए?


306

मैंने एक संग्रह बनाया जिसके लिए मैं एक एसटीएल-शैली, रैंडम-एक्सेस इटरेटर प्रदान करना चाहता हूं। मैं एक इटरेटर के उदाहरण के कार्यान्वयन के लिए चारों ओर खोज रहा था, लेकिन मुझे कोई नहीं मिला। मुझे पता है कि ऑपरेटरों []और *ऑपरेटरों के ओवरलोड की आवश्यकता क्या है । "एसटीएल-शैली" होने के लिए एक पुनरावृत्ति के लिए क्या आवश्यकताएं हैं और यदि (यदि कोई है) तो बचने के लिए कुछ अन्य नुकसान क्या हैं?

अतिरिक्त संदर्भ: यह एक पुस्तकालय के लिए है और जब तक मुझे वास्तव में आवश्यकता नहीं होती है, मैं इस पर कोई निर्भरता का परिचय नहीं देना चाहता। मैं अपने स्वयं के संग्रह को एक ही संकलक के साथ C ++ 03 और C ++ 11 के बीच द्विआधारी संगतता प्रदान करने में सक्षम होने के लिए लिखता हूं (इसलिए कोई एसटीएल जो शायद टूट जाएगा)।


13
+1! अच्छा प्रश्न। मैं एक ही बात सोच रहा हूँ। Boost.Iterator के आधार पर किसी चीज़ को एक साथ फ्लिक करना काफी आसान है, लेकिन अगर आप इसे स्क्रैच से लागू करते हैं तो आवश्यकताओं की एक सूची ढूंढना आश्चर्यजनक रूप से कठिन है।
jalf

2
यह भी याद रखें कि आपके पुनरावृत्तियों को SCARY होना चाहिए। boost.org/doc/libs/1_55_0/doc/html/intrusive/…
alfC

जवाबों:


232

http://www.cplusplus.com/reference/std/iterator/ में एक आसान चार्ट है जो C ++ 11 मानक के C 24.2.2 के विवरण का विवरण देता है। मूल रूप से, पुनरावृत्तियों में टैग होते हैं जो वैध कार्यों का वर्णन करते हैं, और टैग में एक पदानुक्रम होता है। नीचे विशुद्ध रूप से प्रतीकात्मक है, ये वर्ग वास्तव में ऐसे नहीं हैं।

iterator {
    iterator(const iterator&);
    ~iterator();
    iterator& operator=(const iterator&);
    iterator& operator++(); //prefix increment
    reference operator*() const;
    friend void swap(iterator& lhs, iterator& rhs); //C++11 I think
};

input_iterator : public virtual iterator {
    iterator operator++(int); //postfix increment
    value_type operator*() const;
    pointer operator->() const;
    friend bool operator==(const iterator&, const iterator&);
    friend bool operator!=(const iterator&, const iterator&); 
};
//once an input iterator has been dereferenced, it is 
//undefined to dereference one before that.

output_iterator : public virtual iterator {
    reference operator*() const;
    iterator operator++(int); //postfix increment
};
//dereferences may only be on the left side of an assignment
//once an output iterator has been dereferenced, it is 
//undefined to dereference one before that.

forward_iterator : input_iterator, output_iterator {
    forward_iterator();
};
//multiple passes allowed

bidirectional_iterator : forward_iterator {
    iterator& operator--(); //prefix decrement
    iterator operator--(int); //postfix decrement
};

random_access_iterator : bidirectional_iterator {
    friend bool operator<(const iterator&, const iterator&);
    friend bool operator>(const iterator&, const iterator&);
    friend bool operator<=(const iterator&, const iterator&);
    friend bool operator>=(const iterator&, const iterator&);

    iterator& operator+=(size_type);
    friend iterator operator+(const iterator&, size_type);
    friend iterator operator+(size_type, const iterator&);
    iterator& operator-=(size_type);  
    friend iterator operator-(const iterator&, size_type);
    friend difference_type operator-(iterator, iterator);

    reference operator[](size_type) const;
};

contiguous_iterator : random_access_iterator { //C++17
}; //elements are stored contiguously in memory.

आप या तो विशेषज्ञ std::iterator_traits<youriterator>टाइप कर सकते हैं , या उसी टाइप टाइप को इट्रेटर में डाल सकते हैं , या इनसे इनहेरिट कर सकते हैं std::iterator(जिसमें ये टाइपडेफ हैं)। मैं दूसरे विकल्प को पसंद करता हूं, stdनामस्थान में चीजों को बदलने से और पठनीयता से बचने के लिए, लेकिन ज्यादातर लोग विरासत में मिलते हैं std::iterator

struct std::iterator_traits<youriterator> {        
    typedef ???? difference_type; //almost always ptrdiff_t
    typedef ???? value_type; //almost always T
    typedef ???? reference; //almost always T& or const T&
    typedef ???? pointer; //almost always T* or const T*
    typedef ???? iterator_category;  //usually std::forward_iterator_tag or similar
};

नोट iterator_category से एक होना चाहिए std::input_iterator_tag, std::output_iterator_tag, std::forward_iterator_tag, std::bidirectional_iterator_tag, या std::random_access_iterator_tagहै, जो आवश्यकताओं को अपने इटरेटर को संतुष्ट करता है पर निर्भर करता है। अपने इटरेटर आधार पर, आप विशेषज्ञता चुन सकते std::next, std::prev, std::advance, और std::distanceसाथ ही, लेकिन यह शायद ही कभी की जरूरत है। में अत्यंत दुर्लभ मामलों आप विशेषज्ञ करना चाह सकते हैं std::beginऔर std::end

आपके कंटेनर में संभवतः एक भी होना चाहिए const_iterator, जो निरंतर डेटा के लिए एक (संभवतः परस्पर) चलने वाला है जो आपके समान है iteratorसिवाय इसके कि यह एक से अनुमानित रूप से रचनात्मक iteratorहोना चाहिए और उपयोगकर्ताओं को डेटा को संशोधित करने में असमर्थ होना चाहिए। इसके आंतरिक पॉइंटर को गैर-स्थिर डेटा के लिए सूचक होना आम है, और कोड डुप्लीकेशन को कम करने के लिए iteratorइनहेरिट const_iteratorकिया गया है।

अपने खुद के एसटीएल कंटेनर लेखन में मेरी पोस्ट में एक अधिक पूर्ण कंटेनर / पुनरावृत्त प्रोटोटाइप है।


2
std::iterator_traitsअपने आप को टाइप करने वालों को विशेष रूप से परिभाषित करने या परिभाषित करने के अलावा, आप केवल std::iteratorइसके टेम्पलेट मापदंडों के आधार पर, जो आपके लिए इसे परिभाषित करते हैं, से प्राप्त कर सकते हैं ।
क्रिश्चियन राऊ

3
@ लोकीअस्टारी: पूर्ण प्रलेखन काफी व्यापक है (मसौदे में 40 पृष्ठ), और स्टोटर ओवरफ्लो के दायरे में नहीं। हालाँकि, मैंने पुनरावृति टैग्स का विवरण देते हुए और जानकारी जोड़ी const_iterator। मेरी पोस्ट में और क्या कमी थी? आपको लगता है कि वर्ग में जोड़ने के लिए और अधिक है, लेकिन सवाल विशेष रूप से पुनरावृत्तियों को लागू करने के बारे में है।
मूविंग डक

5
std::iteratorC ++ 17 में पदावनत करने का प्रस्ताव था ; यह नहीं था, लेकिन मैं इस पर ज्यादा समय तक नहीं टिकूंगा।
einpoklum

2
@ Einpoklum की टिप्पणी के लिए एक अद्यतन: std::iteratorसब के बाद हटा दिया गया था।
scry

1
@ जोनाथनली: वाह, operator boolयह अविश्वसनीय रूप से खतरनाक है। कोई व्यक्ति किसी सीमा के अंत का पता लगाने के लिए इसका उपयोग करने की कोशिश करेगा while(it++), लेकिन यह सब वास्तव में जांचता है कि क्या इटर्मीटर का निर्माण एक पैरामीटर के साथ किया गया था।
मूकिंग डक

16

Iterator_facade प्रलेखन Boost.Iterator से क्या एक लिंक्ड सूची के लिए iterators को लागू करने पर एक अच्छा ट्यूटोरियल की तरह दिखता है प्रदान करता है। क्या आप अपने कंटेनर पर रैंडम-एक्सेस इट्रेटर बनाने के लिए शुरुआती बिंदु के रूप में इसका उपयोग कर सकते हैं?

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


10

थॉमस बेकर ने यहां इस विषय पर एक उपयोगी लेख लिखा ।

यह भी (शायद सरल) दृष्टिकोण था जो पहले एसओ पर दिखाई दिया था: कस्टम पुनरावृत्तियों और कॉन्स्टीट्यूटर्स को सही ढंग से कैसे लागू किया जाए?


10

यहाँ कच्चे सूचक इटेरेटर का नमूना है।

आपको कच्चे पॉइंटर्स के साथ काम करने के लिए पुनरावृत्ति वर्ग का उपयोग नहीं करना चाहिए!

#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <assert.h>

template<typename T>
class ptr_iterator
    : public std::iterator<std::forward_iterator_tag, T>
{
    typedef ptr_iterator<T>  iterator;
    pointer pos_;
public:
    ptr_iterator() : pos_(nullptr) {}
    ptr_iterator(T* v) : pos_(v) {}
    ~ptr_iterator() {}

    iterator  operator++(int) /* postfix */         { return pos_++; }
    iterator& operator++()    /* prefix */          { ++pos_; return *this; }
    reference operator* () const                    { return *pos_; }
    pointer   operator->() const                    { return pos_; }
    iterator  operator+ (difference_type v)   const { return pos_ + v; }
    bool      operator==(const iterator& rhs) const { return pos_ == rhs.pos_; }
    bool      operator!=(const iterator& rhs) const { return pos_ != rhs.pos_; }
};

template<typename T>
ptr_iterator<T> begin(T *val) { return ptr_iterator<T>(val); }


template<typename T, typename Tsize>
ptr_iterator<T> end(T *val, Tsize size) { return ptr_iterator<T>(val) + size; }

रॉ पॉइंटर रेंज आधारित लूप वर्कअराउंड। कृपया, मुझे सही करें, अगर कच्चे सूचक से रेंज आधारित लूप बनाने का बेहतर तरीका है।

template<typename T>
class ptr_range
{
    T* begin_;
    T* end_;
public:
    ptr_range(T* ptr, size_t length) : begin_(ptr), end_(ptr + length) { assert(begin_ <= end_); }
    T* begin() const { return begin_; }
    T* end() const { return end_; }
};

template<typename T>
ptr_range<T> range(T* ptr, size_t length) { return ptr_range<T>(ptr, length); }

और सरल परीक्षण

void DoIteratorTest()
{
    const static size_t size = 10;
    uint8_t *data = new uint8_t[size];
    {
        // Only for iterator test
        uint8_t n = '0';
        auto first = begin(data);
        auto last = end(data, size);
        for (auto it = first; it != last; ++it)
        {
            *it = n++;
        }

        // It's prefer to use the following way:
        for (const auto& n : range(data, size))
        {
            std::cout << " char: " << static_cast<char>(n) << std::endl;
        }
    }
    {
        // Only for iterator test
        ptr_iterator<uint8_t> first(data);
        ptr_iterator<uint8_t> last(first + size);
        std::vector<uint8_t> v1(first, last);

        // It's prefer to use the following way:
        std::vector<uint8_t> v2(data, data + size);
    }
    {
        std::list<std::vector<uint8_t>> queue_;
        queue_.emplace_back(begin(data), end(data, size));
        queue_.emplace_back(data, data + size);
    }
}

5

सबसे पहले आप विभिन्न कार्यों की सूची के लिए यहाँ देख सकते हैं जिन्हें व्यक्तिगत पुनरावृत्त प्रकारों का समर्थन करने की आवश्यकता है।

इसके बाद, जब आपने अपना इटैटर वर्ग बनाया है तो आपको इसके लिए या तो विशेषज्ञ की आवश्यकता है std::iterator_traitsऔर कुछ आवश्यक typedefएस (जैसे iterator_categoryया value_type) प्रदान करना है या वैकल्पिक रूप से इसे प्राप्त करना है std::iterator, जो typedefआपके लिए आवश्यक एस को परिभाषित करता है और इसलिए इसे डिफ़ॉल्ट रूप से उपयोग किया जा सकता है std::iterator_traits

अस्वीकरण: मुझे पता है कि कुछ लोगों को यह पसंद नहीं cplusplus.comहै, लेकिन वे इस पर कुछ बहुत उपयोगी जानकारी प्रदान करते हैं।


मुझे वास्तव में अधिशेष विवाद के लिए अधिशेष नहीं मिलता है, वे दोनों अच्छे हैं और कई चीजें गायब हैं। हालाँकि, C ++ एकमात्र भाषा है जहाँ मानक पुस्तकालय पुनरावृत्तियों को लागू करना एक नरक XD है। अधिकांश समय एक
इटैलर

@GameDeveloper इस टेम्प्लेट लाइब्रेरी की जाँच करें जिसे मैंने iterators लागू करने के लिए लिखा था: github.com/VinGarcia/Simple-Iterator-Template । यह बहुत सरल है और इसे लिखने के लिए कोड की केवल 10 पंक्तियों की आवश्यकता होती है।
विनग्रेसिया

अच्छा वर्ग, मैं इसकी सराहना करता हूं, शायद यह गैर-एसटीएल कंटेनरों (ईए_एसटीएल, यूई 4) के साथ संकलित करने के लिए इसे पोर्ट करने के लायक है .. इस पर विचार करें! :)
कॉफीड्यूलेटर

वैसे भी, अगर एकमात्र कारण यह है कि cplusplus.com कुछ वास्तव में उपयोगी जानकारी प्रदान करता है, तो cppreference.com अधिक उपयोगी जानकारी प्रदान करता है ...
LF

@ एलएफ फिर समय में वापस जाने के लिए स्वतंत्र महसूस करता है और उस जानकारी को साइट के 2011 संस्करण में जोड़ता है। ;-)
क्रिश्चियन राऊ

3

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

वैसे भी, मैंने देखा EASTL को । कंटेनरों के बारे में एक टन सीखने के अलावा कि मैंने कभी भी यह सब स्टाल कंटेनरों का उपयोग करके या अपने स्नातक पाठ्यक्रमों के माध्यम से नहीं सीखा । मुख्य कारण यह है कि EASTL stl समकक्ष की तुलना में अधिक पठनीय है (मैंने पाया कि यह केवल सभी मैक्रोज़ और सीधे आगे की कोडिंग शैली की कमी के कारण है)। वहाँ कुछ icky चीजें हैं (जैसे अपवादों के लिए #ifdefs) लेकिन आपको अभिभूत करने के लिए कुछ भी नहीं।

जैसा कि दूसरों ने उल्लेख किया है, itplusators और कंटेनरों पर cplusplus.com के संदर्भ को देखें।


3

मैं कई अलग-अलग पाठ सरणियों पर पुनरावृति करने में सक्षम होने की समस्या को हल करने की कोशिश कर रहा था, जिनमें से सभी को एक मेमोरी रेजिडेंट डेटाबेस के भीतर संग्रहीत किया जाता है यह एक बड़ा है struct

निम्नलिखित MFC परीक्षण अनुप्रयोग पर Visual Studio 2017 सामुदायिक संस्करण का उपयोग करके काम किया गया था। मैं इसे एक उदाहरण के रूप में शामिल कर रहा हूं क्योंकि यह पोस्टिंग कई में से एक थी जो मैंने पूरी की, जो कुछ मदद प्रदान करती थी, अभी भी मेरी जरूरतों के लिए अपर्याप्त थी।

structयुक्त स्मृति निवासी डेटा निम्नलिखित की तरह कुछ देखा। मैंने अधिकांश तत्वों को संक्षिप्तता के लिए हटा दिया है और इसमें प्रयुक्त प्रीप्रोसेसर डिफाइन को भी शामिल नहीं किया है (उपयोग में एसडीके सी के साथ-साथ सी ++ और पुराना है)।

मुझे जो करने में दिलचस्पी थी, वह विभिन्न WCHARदो आयामी सरणियों के लिए पुनरावृत्तियां कर रहा था जिसमें पाठविज्ञान के लिए पाठ तार शामिल थे।

typedef struct  tagUNINTRAM {
    // stuff deleted ...
    WCHAR   ParaTransMnemo[MAX_TRANSM_NO][PARA_TRANSMNEMO_LEN]; /* prog #20 */
    WCHAR   ParaLeadThru[MAX_LEAD_NO][PARA_LEADTHRU_LEN];   /* prog #21 */
    WCHAR   ParaReportName[MAX_REPO_NO][PARA_REPORTNAME_LEN];   /* prog #22 */
    WCHAR   ParaSpeMnemo[MAX_SPEM_NO][PARA_SPEMNEMO_LEN];   /* prog #23 */
    WCHAR   ParaPCIF[MAX_PCIF_SIZE];            /* prog #39 */
    WCHAR   ParaAdjMnemo[MAX_ADJM_NO][PARA_ADJMNEMO_LEN];   /* prog #46 */
    WCHAR   ParaPrtModi[MAX_PRTMODI_NO][PARA_PRTMODI_LEN];  /* prog #47 */
    WCHAR   ParaMajorDEPT[MAX_MDEPT_NO][PARA_MAJORDEPT_LEN];    /* prog #48 */
    //  ... stuff deleted
} UNINIRAM;

वर्तमान दृष्टिकोण प्रत्येक ऐरे के लिए एक प्रॉक्सी क्लास को परिभाषित करने के लिए टेम्पलेट का उपयोग करना है और फिर एक एकल इटैटर क्लास है जिसका उपयोग किसी विशेष सरणी पर सरणी को दर्शाने वाली प्रॉक्सी ऑब्जेक्ट का उपयोग करके पुन: व्यवस्थित करने के लिए किया जा सकता है।

मेमोरी रेजिडेंट डेटा की एक कॉपी एक ऑब्जेक्ट में संग्रहित की जाती है, जो मेमोरी रेजिडेंट डेटा को / डिस्क से पढ़ने और लिखने को हैंडल करती है। इस वर्ग CFileParaमें टेम्प्लेटेड प्रॉक्सी क्लास शामिल है (MnemonicIteratorDimSize और उप वर्ग जिसमें से यह व्युत्पन्न है, MnemonicIteratorDimSizeBase) और पुनरावृत्त वर्ग, शामिल हैं MnemonicIterator

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

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

const static DWORD_PTR dwId_TransactionMnemonic = 1;
const static DWORD_PTR dwId_ReportMnemonic = 2;
const static DWORD_PTR dwId_SpecialMnemonic = 3;
const static DWORD_PTR dwId_LeadThroughMnemonic = 4;

प्रॉक्सी क्लास

टेम्प्लेटेड प्रॉक्सी क्लास और उसका बेस क्लास इस प्रकार है। मुझे कई प्रकार के wchar_tपाठ स्ट्रिंग सरणियों को समायोजित करने की आवश्यकता थी । दो आयामी सरणियों में अलग-अलग संख्याओं की संज्ञाएं थीं, जो कि मेनेमोनिक के प्रकार (उद्देश्य) पर निर्भर करती हैं और विभिन्न प्रकार की वर्णव्यवस्था अलग-अलग अधिकतम लंबाई की होती हैं, जो पांच पाठ पात्रों और बीस पाठ पात्रों के बीच भिन्न होती हैं। व्युत्पन्न छद्म वर्ग के लिए टेम्प्लेट एक प्राकृतिक फिट था, जिसमें प्रत्येक मेनेमिक में अधिकतम संख्या में वर्णों की आवश्यकता होती थी। प्रॉक्सी ऑब्जेक्ट बनने के बाद, हम तब SetRange()वास्तविक मेनेमोनिक सरणी और उसकी सीमा को निर्दिष्ट करने के लिए विधि का उपयोग करते हैं ।

// proxy object which represents a particular subsection of the
// memory resident database each of which is an array of wchar_t
// text arrays though the number of array elements may vary.
class MnemonicIteratorDimSizeBase
{
    DWORD_PTR  m_Type;

public:
    MnemonicIteratorDimSizeBase(DWORD_PTR x) { }
    virtual ~MnemonicIteratorDimSizeBase() { }

    virtual wchar_t *begin() = 0;
    virtual wchar_t *end() = 0;
    virtual wchar_t *get(int i) = 0;
    virtual int ItemSize() = 0;
    virtual int ItemCount() = 0;

    virtual DWORD_PTR ItemType() { return m_Type; }
};

template <size_t sDimSize>
class MnemonicIteratorDimSize : public MnemonicIteratorDimSizeBase
{
    wchar_t    (*m_begin)[sDimSize];
    wchar_t    (*m_end)[sDimSize];

public:
    MnemonicIteratorDimSize(DWORD_PTR x) : MnemonicIteratorDimSizeBase(x), m_begin(0), m_end(0) { }
    virtual ~MnemonicIteratorDimSize() { }

    virtual wchar_t *begin() { return m_begin[0]; }
    virtual wchar_t *end() { return m_end[0]; }
    virtual wchar_t *get(int i) { return m_begin[i]; }

    virtual int ItemSize() { return sDimSize; }
    virtual int ItemCount() { return m_end - m_begin; }

    void SetRange(wchar_t (*begin)[sDimSize], wchar_t (*end)[sDimSize]) {
        m_begin = begin; m_end = end;
    }

};

द इटरेटर क्लास

पुनरावृति वर्ग ही इस प्रकार है। यह वर्ग केवल मूल फॉरवर्ड इटरेटर कार्यक्षमता प्रदान करता है जो इस समय की आवश्यकता है। हालाँकि मुझे उम्मीद है कि जब मुझे इससे कुछ अतिरिक्त की आवश्यकता होगी तो यह बदल जाएगा या बढ़ाया जाएगा।

class MnemonicIterator
{
private:
    MnemonicIteratorDimSizeBase   *m_p;  // we do not own this pointer. we just use it to access current item.
    int      m_index;                    // zero based index of item.
    wchar_t  *m_item;                    // value to be returned.

public:
    MnemonicIterator(MnemonicIteratorDimSizeBase *p) : m_p(p) { }
    ~MnemonicIterator() { }

    // a ranged for needs begin() and end() to determine the range.
    // the range is up to but not including what end() returns.
    MnemonicIterator & begin() { m_item = m_p->get(m_index = 0); return *this; }                 // begining of range of values for ranged for. first item
    MnemonicIterator & end() { m_item = m_p->get(m_index = m_p->ItemCount()); return *this; }    // end of range of values for ranged for. item after last item.
    MnemonicIterator & operator ++ () { m_item = m_p->get(++m_index); return *this; }            // prefix increment, ++p
    MnemonicIterator & operator ++ (int i) { m_item = m_p->get(m_index++); return *this; }       // postfix increment, p++
    bool operator != (MnemonicIterator &p) { return **this != *p; }                              // minimum logical operator is not equal to
    wchar_t * operator *() const { return m_item; }                                              // dereference iterator to get what is pointed to
};

प्रॉक्सी ऑब्जेक्ट फैक्ट्री निर्धारित करती है कि एमनेमिक पहचानकर्ता के आधार पर कौन सी वस्तु बनाई जाए। प्रॉक्सी ऑब्जेक्ट बनाया गया है और पॉइंटर लौटाया गया है, मानक बेस क्लास प्रकार है, ताकि एक समान इंटरफ़ेस हो, चाहे अलग-अलग mnemonic वर्गों तक पहुँचा जा रहा हो। SetRange()विधि प्रॉक्सी वस्तु के लिए विशिष्ट सरणी तत्वों प्रॉक्सी का प्रतिनिधित्व करता है और सरणी तत्वों की श्रेणी निर्दिष्ट करने के लिए किया जाता है।

CFilePara::MnemonicIteratorDimSizeBase * CFilePara::MakeIterator(DWORD_PTR x)
{
    CFilePara::MnemonicIteratorDimSizeBase  *mi = nullptr;

    switch (x) {
    case dwId_TransactionMnemonic:
        {
            CFilePara::MnemonicIteratorDimSize<PARA_TRANSMNEMO_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_TRANSMNEMO_LEN>(x);
            mk->SetRange(&m_Para.ParaTransMnemo[0], &m_Para.ParaTransMnemo[MAX_TRANSM_NO]);
            mi = mk;
        }
        break;
    case dwId_ReportMnemonic:
        {
            CFilePara::MnemonicIteratorDimSize<PARA_REPORTNAME_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_REPORTNAME_LEN>(x);
            mk->SetRange(&m_Para.ParaReportName[0], &m_Para.ParaReportName[MAX_REPO_NO]);
            mi = mk;
        }
        break;
    case dwId_SpecialMnemonic:
        {
            CFilePara::MnemonicIteratorDimSize<PARA_SPEMNEMO_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_SPEMNEMO_LEN>(x);
            mk->SetRange(&m_Para.ParaSpeMnemo[0], &m_Para.ParaSpeMnemo[MAX_SPEM_NO]);
            mi = mk;
        }
        break;
    case dwId_LeadThroughMnemonic:
        {
            CFilePara::MnemonicIteratorDimSize<PARA_LEADTHRU_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_LEADTHRU_LEN>(x);
            mk->SetRange(&m_Para.ParaLeadThru[0], &m_Para.ParaLeadThru[MAX_LEAD_NO]);
            mi = mk;
        }
        break;
    }

    return mi;
}

प्रॉक्सी क्लास और इटरेटर का उपयोग करना

छद्म वर्ग और इसके पुनरावृत्ति का उपयोग निम्न लूप में दिखाया गया है जो किसी CListCtrlऑब्जेक्ट में एक प्रकार का कीड़ा के साथ भरने के लिए दिखाया गया है । मैं उपयोग कर रहा हूँ std::unique_ptrताकि जब प्रॉक्सी वर्ग की मुझे आवश्यकता न हो औरstd::unique_ptr गुंजाइश से बाहर हो जाता है, तो मेमोरी को साफ किया जाएगा।

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

    std::unique_ptr<CFilePara::MnemonicIteratorDimSizeBase> pObj(pFile->MakeIterator(m_IteratorType));
    CFilePara::MnemonicIterator pIter(pObj.get());  // provide the raw pointer to the iterator who doesn't own it.

    int i = 0;    // CListCtrl index for zero based position to insert mnemonic.
    for (auto x : pIter)
    {
        WCHAR szText[32] = { 0 };     // Temporary buffer.

        wcsncpy_s(szText, 32, x, pObj->ItemSize());
        m_mnemonicList.InsertItem(i, szText);  i++;
    }

1

और अब लूप के लिए रेंज-बेस्ड के लिए एक की-इट्रेटर।

template<typename C>
class keys_it
{
    typename C::const_iterator it_;
public:
    using key_type        = typename C::key_type;
    using pointer         = typename C::key_type*;
    using difference_type = std::ptrdiff_t;

    keys_it(const typename C::const_iterator & it) : it_(it) {}

    keys_it         operator++(int               ) /* postfix */ { return it_++         ; }
    keys_it&        operator++(                  ) /*  prefix */ { ++it_; return *this  ; }
    const key_type& operator* (                  ) const         { return it_->first    ; }
    const key_type& operator->(                  ) const         { return it_->first    ; }
    keys_it         operator+ (difference_type v ) const         { return it_ + v       ; }
    bool            operator==(const keys_it& rhs) const         { return it_ == rhs.it_; }
    bool            operator!=(const keys_it& rhs) const         { return it_ != rhs.it_; }
};

template<typename C>
class keys_impl
{
    const C & c;
public:
    keys_impl(const C & container) : c(container) {}
    const keys_it<C> begin() const { return keys_it<C>(std::begin(c)); }
    const keys_it<C> end  () const { return keys_it<C>(std::end  (c)); }
};

template<typename C>
keys_impl<C> keys(const C & container) { return keys_impl<C>(container); }

उपयोग:

std::map<std::string,int> my_map;
// fill my_map
for (const std::string & k : keys(my_map))
{
    // do things
}

यही मैं ढूंढ रहा था। लेकिन किसी के पास नहीं था, ऐसा लगता है।

आपको एक बोनस के रूप में मेरा ओसीडी कोड संरेखण मिलता है।

एक अभ्यास के रूप में, अपने लिए लिखें values(my_map)

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