วิธีการใช้ตัววนซ้ำแบบ STL และหลีกเลี่ยงข้อผิดพลาดทั่วไป?


306

ฉันสร้างคอลเล็กชันที่ฉันต้องการให้ตัววนซ้ำ STL เข้าถึงแบบสุ่ม ฉันค้นหาตัวอย่างการใช้ตัววนซ้ำ แต่ฉันไม่พบสิ่งใด ฉันรู้เกี่ยวกับความจำเป็นในการโอเวอร์โหลด[]และ*ตัวดำเนินการ ข้อกำหนดสำหรับตัววนซ้ำเป็น "ลักษณะ STL" และข้อผิดพลาดอื่น ๆ ที่ควรหลีกเลี่ยง (ถ้ามี) คืออะไร

บริบทเพิ่มเติม: นี่สำหรับห้องสมุดและฉันไม่ต้องการแนะนำการพึ่งพาใด ๆ เว้นแต่ว่าฉันต้องการ ฉันเขียนคอลเลกชันของตัวเองเพื่อให้สามารถใช้งานร่วมกันได้ของไบนารีระหว่าง C ++ 03 และ C ++ 11 ด้วยคอมไพเลอร์เดียวกัน (ดังนั้น STL ที่ไม่น่าจะทำลาย)


13
+1! เป็นคำถามที่ดี ฉันสงสัยในสิ่งเดียวกัน มันง่ายพอที่จะสะบัดอะไรบางอย่างเข้าด้วยกันโดยใช้ Boost.Iterator แต่ก็ยากที่จะหารายการข้อกำหนดถ้าคุณใช้มันตั้งแต่ต้น
jalf

2
โปรดจำไว้ด้วยว่าตัววนซ้ำของคุณจะต้องน่ากลัว boost.org/doc/libs/1_55_0/doc/html/intrusive/ ......
alfC

คำตอบ:


232

http://www.cplusplus.com/reference/std/iterator/มีแผนภูมิที่ใช้งานง่ายซึ่งให้รายละเอียดข้อมูลจำเพาะของ§ 24.2.2 ของมาตรฐาน C ++ 11 โดยทั่วไปตัววนซ้ำมีแท็กที่อธิบายการดำเนินการที่ถูกต้องและแท็กมีลำดับชั้น ด้านล่างเป็นสัญลักษณ์ล้วนๆคลาสเหล่านี้ไม่มีอยู่จริง

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>หรือวาง typedefs เดียวกันใน iterator เองหรือสืบทอดจากstd::iterator(ซึ่งมี typedefs เหล่านี้) ฉันชอบตัวเลือกที่สองที่จะหลีกเลี่ยงการเปลี่ยนสิ่งที่อยู่ในstdnamespace และเพื่อให้สามารถอ่าน 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ขึ้นอยู่กับความต้องการของคุณตอบสนองความ iterator ทั้งนี้ขึ้นอยู่กับ iterator ของคุณคุณอาจเลือกที่จะมีความเชี่ยวชาญstd::next, std::prev, std::advanceและstd::distanceเช่นกัน แต่นี้จะไม่ค่อยจำเป็น ในที่หายากมากๆ กรณีคุณอาจต้องการที่จะมีความเชี่ยวชาญและstd::beginstd::end

คอนเทนเนอร์ของคุณควรมี a const_iteratorซึ่งเป็นตัววนซ้ำ (อาจเปลี่ยนแปลงได้) กับข้อมูลคงที่ซึ่งคล้ายกับของคุณiteratorยกเว้นควรสร้างได้โดยปริยายจาก a iteratorและผู้ใช้ไม่ควรแก้ไขข้อมูล เป็นเรื่องปกติที่ตัวชี้ภายในจะเป็นตัวชี้ไปยังข้อมูลที่ไม่คงที่และมีการiteratorสืบทอดจากconst_iteratorเพื่อลดการทำสำเนารหัส

โพสต์ของฉันที่เขียนคอนเทนเนอร์ STL ของคุณมีต้นแบบ / ตัววนซ้ำที่สมบูรณ์ยิ่งขึ้น


2
นอกเหนือจากผู้เชี่ยวชาญstd::iterator_traitsหรือกำหนดประเภทของตัวเองแล้วคุณยังสามารถได้มาstd::iteratorซึ่งนิยามสิ่งเหล่านั้นให้คุณขึ้นอยู่กับพารามิเตอร์ของเทมเพลต
Christian Rau

3
@LokiAstari: เอกสารฉบับสมบูรณ์ค่อนข้างกว้างขวาง (หน้า 40ish ในแบบร่าง) และไม่อยู่ในขอบเขตของ Stack Overflow แต่ผมเพิ่มข้อมูลเพิ่มเติมรายละเอียดแท็ก iterator const_iteratorและ โพสต์ของฉันขาดอะไรอีก? ดูเหมือนว่าคุณจะมีการเพิ่มในชั้นเรียนมากขึ้น แต่คำถามนั้นเกี่ยวกับการใช้ตัววนซ้ำ
Mooing Duck

5
std::iteratorถูกเสนอให้เลิกใช้ใน C ++ 17 ; มันไม่ใช่ แต่ฉันจะไม่คิดว่ามันอยู่นานกว่านี้อีกแล้ว
einpoklum

2
การอัปเดตความคิดเห็นของ @ einpoklum: std::iteratorเลิกใช้แล้วหลังจากทั้งหมด
Scry

1
@ JonathanLee: ว้าวนั่นoperator boolมันอันตรายอย่างเหลือเชื่อ ใครบางคนจะพยายามใช้สิ่งนั้นเพื่อตรวจหาจุดจบของช่วงwhile(it++)แต่สิ่งที่ตรวจสอบจริงๆคือว่าตัววนซ้ำถูกสร้างด้วยพารามิเตอร์
Mooing Duck

16

เอกสาร iterator_facadeจาก Boost.Iterator ให้สิ่งที่ดูเหมือนว่าการกวดวิชาที่ดีในการดำเนินการ iterators สำหรับรายการที่เชื่อมโยง คุณสามารถใช้สิ่งนี้เป็นจุดเริ่มต้นในการสร้างตัววนซ้ำแบบเข้าถึงได้เหนือคอนเทนเนอร์ของคุณหรือไม่?

หากไม่มีอะไรอื่นคุณสามารถดูฟังก์ชั่นของสมาชิกและพิมพ์คำสั่งที่ให้ไว้iterator_facadeและใช้เป็นจุดเริ่มต้นสำหรับการสร้างของคุณเอง


10

โทมัสเบกเกอร์เขียนบทความที่มีประโยชน์เกี่ยวกับเรื่องนี้ที่นี่

นอกจากนี้ยังมีวิธีนี้ (อาจจะง่ายกว่า) ที่ปรากฏก่อนหน้านี้บน SO: วิธีการใช้ตัววนซ้ำที่กำหนดเองและ const_iterators อย่างถูกต้องได้อย่างไร


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

ครั้งแรกของทั้งหมดที่คุณสามารถดูที่นี่สำหรับรายชื่อของการดำเนินงานต่างๆประเภท iterator บุคคลต้องสนับสนุน

ถัดไปเมื่อคุณได้ทำระดับ iterator คุณคุณจะต้องมีความเชี่ยวชาญทั้งstd::iterator_traitsมันและให้สิ่งที่จำเป็นบางtypedefs (ชอบiterator_categoryหรือvalue_type) หรือหรือเป็นผลมาจากการstd::iteratorที่กำหนดที่จำเป็นสำหรับคุณและดังนั้นจึงสามารถนำมาใช้กับการเริ่มต้นtypedefstd::iterator_traits

การปฏิเสธความรับผิดชอบ:ฉันรู้ว่าบางคนไม่ชอบcplusplus.comมาก แต่พวกเขาให้ข้อมูลที่เป็นประโยชน์บางอย่างเกี่ยวกับเรื่องนี้


ฉันไม่ได้รับข้อโต้แย้ง cplusplus vs cppreference พวกเขาทั้งดีและหายไปหลายอย่าง อย่างไรก็ตาม C ++ เป็นภาษาเดียวที่ใช้ตัวทำซ้ำไลบรารีมาตรฐานคือนรก XD เวลาส่วนใหญ่นั้นง่ายกว่าการเขียนคลาส wrapper บนคอนเทนเนอร์ stl มากกว่าการใช้ iterator XD
CoffeDeveloper

@GameDeveloper ตรวจสอบห้องสมุดแม่แบบนี้ผมเขียนสำหรับการดำเนินการ iterators: github.com/VinGarcia/Simple-Iterator-Template มันง่ายมากและต้องใช้โค้ดประมาณ 10 บรรทัดเท่านั้นในการเขียนตัววนซ้ำ
VinGarcia

ชั้นดีฉันขอขอบคุณมันอาจคุ้มค่าที่จะทำการคอมไพล์ด้วยคอนเทนเนอร์ที่ไม่ใช่ STL (EA_STL, UE4) .. ลองพิจารณาดูสิ! :)
CoffeDeveloper

อย่างไรก็ตามถ้าเหตุผลเดียวคือ cplusplus.com ให้ข้อมูลที่เป็นประโยชน์จริง ๆ cppreference.com ให้ข้อมูลที่เป็นประโยชน์มากกว่า ...
LF

@LF จากนั้นอย่าลังเลที่จะย้อนเวลากลับไปและเพิ่มข้อมูลนั้นลงในไซต์รุ่น 2011 ;-)
Christian Rau

3

ฉันอยู่ในเรือลำเดียวกันกับคุณด้วยเหตุผลที่แตกต่างกัน (การศึกษาบางส่วนข้อ จำกัด บางส่วน) ฉันต้องเขียนคอนเทนเนอร์ทั้งหมดของไลบรารีมาตรฐานอีกครั้งและคอนเทนเนอร์ต้องสอดคล้องกับมาตรฐาน นั่นหมายความว่าถ้าฉันสลับคอนเทนเนอร์ของฉันด้วยเวอร์ชันstlโค้ดจะใช้งานได้เหมือนกัน ซึ่งหมายความว่าฉันต้องเขียนตัววนซ้ำอีกครั้ง

อย่างไรก็ตามผมมองที่EASTL นอกเหนือจากการเรียนรู้เกี่ยวกับภาชนะบรรจุที่ฉันไม่เคยเรียนรู้ตลอดเวลานี้โดยใช้คอนเทนเนอร์stlหรือผ่านหลักสูตรระดับปริญญาตรีของฉัน เหตุผลหลักคือEASTLสามารถอ่านได้ดีกว่าstl (ฉันพบว่านี่เป็นเพียงเพราะขาดมาโครทั้งหมดและรูปแบบการเข้ารหัสที่ตรงไปตรงมา) มีบางสิ่งที่น่ากลัวอยู่ในนั้น (เช่น #ifdefs สำหรับข้อยกเว้น) แต่ไม่มีอะไรจะครอบงำคุณ

ตามที่คนอื่นพูดถึงดูที่การอ้างอิงของ cplusplus.com เกี่ยวกับตัววนซ้ำและคอนเทนเนอร์


3

structผมพยายามที่จะแก้ปัญหาของความสามารถในการย้ำผ่านอาร์เรย์หลายข้อความที่แตกต่างกันซึ่งทั้งหมดจะถูกเก็บไว้ภายในฐานข้อมูลหน่วยความจำที่มีถิ่นที่อยู่เป็นที่มีขนาดใหญ่

ต่อไปนี้เป็นผลงานโดยใช้ Visual Studio 2017 Community Edition ในแอปพลิเคชันทดสอบ MFC ฉันรวมนี่เป็นตัวอย่างเนื่องจากการโพสต์นี้เป็นหนึ่งในหลาย ๆ ที่ฉันวิ่งข้ามที่ให้ความช่วยเหลือ แต่ก็ยังไม่เพียงพอสำหรับความต้องการของฉัน

ข้อมูลstructที่อยู่ในหน่วยความจำประกอบด้วยข้อมูลดังต่อไปนี้ ฉันได้ลบองค์ประกอบส่วนใหญ่เพื่อประโยชน์ของความกะทัดรัดและยังไม่รวมถึงการกำหนด Preprocessor ที่ใช้ (SDK ที่ใช้สำหรับ C และ C ++ และเก่า)

สิ่งที่ฉันสนใจทำคือมีตัววนซ้ำสำหรับ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) และชั้น iterator 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;

ระดับพร็อกซี่

คลาสพร็อกซี templated และคลาสพื้นฐานมีดังนี้ ฉันจำเป็นต้องรองรับอาเรย์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;
    }

};

The Iterator Class

คลาสตัววนซ้ำตัวเองมีดังนี้ คลาสนี้จัดเตรียมฟังก์ชันการทำงานตัวส่งต่อพื้นฐานแบบพื้นฐานซึ่งเป็นสิ่งที่จำเป็นในเวลานี้ อย่างไรก็ตามฉันคาดหวังว่าสิ่งนี้จะเปลี่ยนแปลงหรือขยายออกไปเมื่อฉันต้องการอะไรเพิ่มเติมจากมัน

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
};

โรงงานวัตถุพร็อกซีกำหนดว่าวัตถุใดที่จะสร้างโดยยึดตามตัวช่วยจำ วัตถุพร็อกซีถูกสร้างขึ้นและตัวชี้ที่ส่งคืนเป็นประเภทคลาสฐานมาตรฐานเพื่อให้มีอินเทอร์เฟซที่เหมือนกันไม่ว่าส่วนใดของส่วนช่วยในการจำจะถูกเข้าถึง 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;
}

การใช้ Proxy Class และ Iterator

คลาสพร็อกซีและตัววนซ้ำของมันถูกใช้ตามที่แสดงในลูปต่อไปนี้เพื่อเติมCListCtrlวัตถุที่มีรายการช่วยในการจำ ฉันใช้std::unique_ptrเพื่อที่เมื่อไม่จำเป็นต้องใช้พร็อกซีคลาสอีกต่อไปและไม่อยู่std::unique_ptrในขอบเขตหน่วยความจำจะถูกล้างออก

สิ่งที่ซอร์สโค้ดนี้ทำคือการสร้างวัตถุพร็อกซีสำหรับอาร์เรย์ภายในstructซึ่งสอดคล้องกับตัวช่วยจำที่ระบุ จากนั้นจะสร้างตัววนซ้ำสำหรับวัตถุนั้นใช้ระยะห่าง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
}

นั่นคือสิ่งที่ฉันกำลังมองหา แต่ไม่มีใครมีมันดูเหมือน

คุณจะได้รับการจัดรหัส OCD ของฉันเป็นโบนัส

เป็นแบบฝึกหัดเขียนของคุณเองสำหรับ values(my_map)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.