Làm thế nào để thực hiện một trình lặp kiểu STL và tránh những cạm bẫy phổ biến?


306

Tôi đã tạo một bộ sưu tập mà tôi muốn cung cấp một trình lặp truy cập ngẫu nhiên, truy cập ngẫu nhiên theo kiểu STL. Tôi đã tìm kiếm xung quanh để thực hiện một ví dụ về trình vòng lặp nhưng tôi không tìm thấy. Tôi biết về sự cần thiết của quá tải const []và các *nhà khai thác. Các yêu cầu đối với một trình vòng lặp là "kiểu STL" là gì và một số cạm bẫy khác cần tránh (nếu có) là gì?

Bối cảnh bổ sung: Đây là một thư viện và tôi không muốn giới thiệu bất kỳ sự phụ thuộc nào vào nó trừ khi tôi thực sự cần. Tôi viết bộ sưu tập của riêng mình để có thể cung cấp khả năng tương thích nhị phân giữa C ++ 03 và C ++ 11 với cùng một trình biên dịch (vì vậy không có STL nào có thể phá vỡ).


13
+1! Câu hỏi hay. Tôi đã tự hỏi điều tương tự. Thật dễ dàng để kết hợp một cái gì đó dựa trên Boost.Iterator, nhưng thật khó để tìm thấy một danh sách các yêu cầu nếu bạn thực hiện nó từ đầu.
jalf

2
Cũng cần nhớ rằng các trình vòng lặp của bạn phải là SCary. boost.org/doc/libs/1_55_0/doc/html/intrusive/ Kẻ
alfC

Câu trả lời:


232

http://www.cplusplus.com/reference/std/iterator/ có một biểu đồ tiện dụng, chi tiết các thông số kỹ thuật của § 24.2.2 của tiêu chuẩn C ++ 11. Về cơ bản, các trình vòng lặp có các thẻ mô tả các hoạt động hợp lệ và các thẻ có một hệ thống phân cấp. Dưới đây hoàn toàn là biểu tượng, những lớp này không thực sự tồn tại như vậy.

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.

Bạn có thể chuyên môn hóa std::iterator_traits<youriterator>hoặc đặt cùng một typedefs trong chính trình lặp hoặc thừa kế từ std::iterator(có các typedefs này). Tôi thích tùy chọn thứ hai, để tránh thay đổi mọi thứ trong stdkhông gian tên và để dễ đọc, nhưng hầu hết mọi người đều thừa hưởng 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
};

Lưu ý iterator_category nên là một trong std::input_iterator_tag, std::output_iterator_tag, std::forward_iterator_tag, std::bidirectional_iterator_tag, hoặc std::random_access_iterator_tag, tùy thuộc vào yêu cầu của bạn đáp ứng iterator. Tùy thuộc vào iterator của bạn, bạn có thể chọn chuyên std::next, std::prev, std::advance, và std::distancelà tốt, nhưng điều này hiếm khi cần thiết. Trong trường hợp cực kỳ hiếm bạn có thể muốn chuyên std::beginstd::end.

Container của bạn có lẽ cũng phải có một const_iterator, đó là một trình lặp (có thể thay đổi) thành dữ liệu không đổi tương tự với bạn iteratorngoại trừ nó có thể được xây dựng ngầm từ a iteratorvà người dùng sẽ không thể sửa đổi dữ liệu. Thông thường, con trỏ bên trong của nó là một con trỏ tới dữ liệu không cố định và iteratorđược kế thừa từ const_iteratorđó để giảm thiểu sao chép mã.

Bài đăng của tôi tại Writing STL Container của riêng bạn có một nguyên mẫu container / iterator hoàn chỉnh hơn.


2
Ngoài việc chuyên môn hóa std::iterator_traitshoặc tự xác định typedefs, bạn cũng có thể lấy từ std::iteratorđó, định nghĩa chúng cho bạn, tùy thuộc vào các tham số mẫu của nó.
Christian Rau

3
@LokiAstari: Tài liệu đầy đủ khá rộng (40 trang trong bản nháp) và không nằm trong phạm vi của Stack Overflow. Tuy nhiên, tôi đã thêm thông tin chi tiết các thẻ lặp và const_iterator. Bài viết của tôi còn thiếu gì nữa? Bạn dường như ngụ ý có nhiều thứ để thêm vào lớp, nhưng câu hỏi cụ thể là về việc thực hiện các trình vòng lặp.
Vịt Mooing

5
std::iteratorđã được đề xuất để không được chấp nhận trong C ++ 17 ; không phải vậy, nhưng tôi sẽ không ngân hàng vì nó tồn tại lâu hơn nữa.
einpoklum

2
Một bản cập nhật cho nhận xét của @ einpoklum: std::iteratorđã bị phản đối sau tất cả.
scry

1
@JonathanLee: Wow, điều đó operator boolcực kỳ nguy hiểm. Ai đó sẽ cố gắng sử dụng điều đó để phát hiện sự kết thúc của một phạm vi while(it++), nhưng tất cả những gì nó thực sự kiểm tra là nếu trình vòng lặp được xây dựng với một tham số.
Vịt Mooing

16

Các tài liệu iterator_facade từ Boost.Iterator cung cấp những gì trông giống như một hướng dẫn tốt đẹp về việc thực hiện lặp cho một danh sách liên kết. Bạn có thể sử dụng nó như một điểm khởi đầu để xây dựng một trình vòng lặp truy cập ngẫu nhiên trên container của bạn không?

Nếu không có gì khác, bạn có thể xem các hàm thành viên và typedefs được cung cấp bởi iterator_facadevà sử dụng nó như một điểm khởi đầu để xây dựng của riêng bạn.



10

Đây là mẫu của trình lặp con trỏ thô.

Bạn không nên sử dụng lớp lặp để làm việc với con trỏ thô!

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

Con trỏ thô dựa trên vòng lặp giải pháp. Xin vui lòng, sửa cho tôi, nếu có cách tốt hơn để tạo vòng lặp dựa trên phạm vi từ con trỏ thô.

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

Và kiểm tra đơn giản

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

Trước hết bạn có thể xem ở đây một danh sách các hoạt động khác nhau mà các loại trình vòng lặp riêng lẻ cần hỗ trợ.

Tiếp theo, khi bạn đã tạo lớp lặp của mình, bạn cần phải chuyên môn hóa std::iterator_traitsnó và cung cấp một số typedefs (như iterator_categoryhoặc value_type) cần thiết hoặc lấy từ std::iteratorđó, từ đó xác định typedefs cần thiết cho bạn và do đó có thể được sử dụng với mặc định std::iterator_traits.

từ chối trách nhiệm: Tôi biết một số người không thích cplusplus.comđiều đó, nhưng họ cung cấp một số thông tin thực sự hữu ích về điều này.


Tôi thực sự không có tranh chấp cplusplus vs cppreference, cả hai đều tốt và thiếu nhiều thứ. Tuy nhiên, C ++ là ngôn ngữ duy nhất mà việc thực hiện các trình vòng lặp thư viện tiêu chuẩn là một XD địa ngục. Hầu hết thời gian đơn giản hơn khi viết một lớp bao bọc trên một thùng chứa stl hơn là thực hiện một trình vòng lặp XD
CoffeDeveloper

@GameDeveloper kiểm tra thư viện mẫu này tôi đã viết để triển khai các trình vòng lặp: github.com/VinGarcia/Simple-Iterator-Template . Nó rất đơn giản và chỉ cần khoảng 10 dòng mã để viết một trình vòng lặp.
VinGarcia

Lớp đẹp, tôi đánh giá cao nó, có lẽ đáng để chuyển nó để biên dịch với các thùng chứa không phải STL (EA_STL, UE4) .. Hãy xem xét nó! :)
Nhà phát triển Coffe

Dù sao, nếu lý do duy nhất là cplusplus.com cung cấp một số thông tin thực sự hữu ích, cppreference.com cung cấp nhiều thông tin hữu ích hơn ...
LF

@LF Sau đó, vui lòng quay ngược thời gian và thêm thông tin đó vào phiên bản 2011 của trang web. ;-)
Christian Rau

3

Tôi đã / đang ở trong cùng một chiếc thuyền với bạn vì những lý do khác nhau (một phần giáo dục, một phần hạn chế). Tôi đã phải viết lại tất cả các container của thư viện tiêu chuẩn và các container phải tuân theo tiêu chuẩn. Điều đó có nghĩa là, nếu tôi trao đổi container của mình với phiên bản stl , mã sẽ hoạt động như nhau. Điều đó cũng có nghĩa là tôi phải viết lại các vòng lặp.

Dù sao, tôi đã nhìn vào EASTL . Ngoài việc học rất nhiều về các container mà tôi chưa bao giờ học suốt thời gian này bằng cách sử dụng các thùng chứa stl hoặc thông qua các khóa học đại học của tôi. Lý do chính là EASTL dễ đọc hơn bản sao stl (tôi thấy điều này đơn giản là vì thiếu tất cả các macro và kiểu mã hóa thẳng về phía trước). Có một số điều kỳ diệu trong đó (như #ifdefs cho các trường hợp ngoại lệ) nhưng không có gì để áp đảo bạn.

Như những người khác đã đề cập, hãy xem tài liệu tham khảo của cplusplus.com về các trình vòng lặp và các thùng chứa.


3

Tôi đã cố gắng giải quyết vấn đề có thể lặp lại qua một số mảng văn bản khác nhau, tất cả chúng được lưu trữ trong một cơ sở dữ liệu lưu trữ bộ nhớ lớn struct.

Sau đây đã được thực hiện bằng Visual Studio 2017 Community Edition trên ứng dụng thử nghiệm MFC. Tôi lấy ví dụ này làm ví dụ vì bài đăng này là một trong số đó tôi đã chạy qua cung cấp một số trợ giúp nhưng vẫn không đủ cho nhu cầu của tôi.

Việc structchứa dữ liệu cư trú bộ nhớ trông giống như sau. Tôi đã loại bỏ hầu hết các yếu tố vì lý do ngắn gọn và cũng không bao gồm Định nghĩa tiền xử lý được sử dụng (SDK được sử dụng là cho C cũng như C ++ và đã cũ).

Điều tôi quan tâm là làm các trình vòng lặp cho các WCHARmảng hai chiều khác nhau có chứa các chuỗi văn bản cho việc ghi nhớ.

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;

Cách tiếp cận hiện tại là sử dụng một khuôn mẫu để xác định một lớp proxy cho từng mảng và sau đó có một lớp lặp duy nhất có thể được sử dụng để lặp qua một mảng cụ thể bằng cách sử dụng một đối tượng proxy đại diện cho mảng.

Một bản sao của dữ liệu lưu trú bộ nhớ được lưu trữ trong một đối tượng xử lý việc đọc và ghi dữ liệu lưu trữ bộ nhớ từ / vào đĩa. Lớp này, CFileParachứa lớp proxy templated ( MnemonicIteratorDimSizevà lớp con mà nó được dẫn xuất, MnemonicIteratorDimSizeBase) và lớp iterator , MnemonicIterator.

Đối tượng proxy được tạo được gắn vào một đối tượng lặp để truy cập thông tin cần thiết thông qua một giao diện được mô tả bởi một lớp cơ sở mà từ đó tất cả các lớp proxy được dẫn xuất. Kết quả là có một loại lớp lặp duy nhất có thể được sử dụng với một số lớp proxy khác nhau vì các lớp proxy khác nhau đều hiển thị cùng một giao diện, giao diện của lớp cơ sở proxy.

Điều đầu tiên là tạo ra một tập hợp các mã định danh sẽ được cung cấp cho một nhà máy lớp để tạo đối tượng proxy cụ thể cho kiểu ghi nhớ đó. Các mã định danh này được sử dụng như một phần của giao diện người dùng để xác định dữ liệu cung cấp cụ thể mà người dùng quan tâm để xem và có thể sửa đổi.

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;

Lớp Proxy

Lớp proxy templated và lớp cơ sở của nó như sau. Tôi cần phải chứa một số loại wchar_tmảng chuỗi văn bản khác nhau . Mảng hai chiều có số lượng ghi nhớ khác nhau, tùy thuộc vào loại (mục đích) của kiểu ghi nhớ và các loại ghi nhớ khác nhau có độ dài tối đa khác nhau, khác nhau giữa năm ký tự văn bản và hai mươi ký tự văn bản. Các mẫu cho lớp proxy dẫn xuất là phù hợp tự nhiên với mẫu yêu cầu số lượng ký tự tối đa trong mỗi lần ghi nhớ. Sau khi đối tượng proxy được tạo, sau đó chúng tôi sử dụng SetRange()phương thức để chỉ định mảng ghi nhớ thực tế và phạm vi của nó.

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

};

Lớp lặp

Bản thân lớp lặp như sau. Lớp này chỉ cung cấp chức năng lặp chuyển tiếp cơ bản, đó là tất cả những gì cần thiết tại thời điểm này. Tuy nhiên tôi hy vọng rằng điều này sẽ thay đổi hoặc được gia hạn khi tôi cần một cái gì đó bổ sung từ nó.

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

Nhà máy đối tượng proxy xác định đối tượng nào sẽ được tạo dựa trên định danh ghi nhớ. Đối tượng proxy được tạo và con trỏ trả về là loại lớp cơ sở tiêu chuẩn để có giao diện thống nhất bất kể phần nào trong các phần ghi nhớ khác nhau đang được truy cập. Các SetRange()phương pháp được sử dụng để xác định đối tượng proxy phần tử mảng cụ proxy đại diện và phạm vi của các phần tử mảng.

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

Sử dụng lớp Proxy và Iterator

Lớp proxy và trình vòng lặp của nó được sử dụng như được hiển thị trong vòng lặp sau để điền vào một CListCtrlđối tượng với một danh sách ghi nhớ. Tôi đang sử dụng std::unique_ptrđể khi lớp proxy tôi không còn cần thiết và std::unique_ptrvượt quá phạm vi, bộ nhớ sẽ bị xóa.

Những gì mã nguồn này làm là tạo một đối tượng proxy cho mảng bên trong structtương ứng với mã định danh ghi nhớ được chỉ định. Sau đó, nó tạo ra một trình vòng lặp cho đối tượng đó, sử dụng một phạm vi forđể điền vào CListCtrlđiều khiển và sau đó dọn sạch. Đây là tất cả các wchar_tchuỗi văn bản thô có thể chính xác là số lượng phần tử mảng, vì vậy chúng tôi sao chép chuỗi vào bộ đệm tạm thời để đảm bảo văn bản không bị chấm dứ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

Và bây giờ là một trình vòng lặp khóa cho vòng lặp dựa trên phạm vi.

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

Sử dụng:

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

Đó là những gì tôi đang tìm kiếm. Nhưng không ai có nó, dường như.

Bạn nhận được liên kết mã OCD của tôi như là một phần thưởng.

Như một bài tập, viết của riêng bạn cho values(my_map)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.