Bagaimana menerapkan iterator gaya-STL dan menghindari perangkap umum?

306

Saya membuat koleksi yang ingin saya berikan iterator acak gaya STL. Saya mencari-cari contoh implementasi iterator tapi saya tidak menemukannya. Saya tahu tentang perlunya overload const []dan *operator. Apa persyaratan untuk iterator untuk menjadi "STL-style" dan apa beberapa perangkap lain yang harus dihindari (jika ada)?

Konteks tambahan: Ini untuk perpustakaan dan saya tidak ingin memperkenalkan ketergantungan padanya kecuali saya benar-benar perlu. Saya menulis koleksi saya sendiri untuk dapat memberikan kompatibilitas biner antara C ++ 03 dan C ++ 11 dengan kompiler yang sama (jadi tidak ada STL yang mungkin akan rusak).

Tamás Szelei
sumber
13
+1! Pertanyaan yang bagus Saya bertanya-tanya hal yang sama. Cukup mudah untuk menjentikkan sesuatu bersama-sama berdasarkan Boost.Iterator, tetapi sangat sulit untuk hanya menemukan daftar persyaratan jika Anda menerapkannya dari awal.
jalf
2
Ingat juga bahwa iterator Anda harus MENAKUTKAN. boost.org/doc/libs/1_55_0/doc/html/intrusive/…
alfC

Jawaban:

232

http://www.cplusplus.com/reference/std/iterator/ memiliki bagan praktis yang merinci spesifikasi § 24.2.2 dari standar C ++ 11. Pada dasarnya, iterator memiliki tag yang menggambarkan operasi yang valid, dan tag memiliki hierarki. Di bawah ini murni simbolis, kelas-kelas ini sebenarnya tidak ada.

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.

Anda dapat mengkhususkan std::iterator_traits<youriterator>, atau memasukkan typedef yang sama di iterator itu sendiri, atau mewarisi dari std::iterator(yang memiliki typedef ini). Saya lebih suka opsi kedua, untuk menghindari mengubah hal-hal di stdnamespace, dan untuk keterbacaan, tetapi kebanyakan orang mewarisi dari 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
};

Perhatikan iterator_category harus menjadi salah satu std::input_iterator_tag, std::output_iterator_tag, std::forward_iterator_tag, std::bidirectional_iterator_tag, atau std::random_access_iterator_tag, tergantung pada kebutuhan iterator Anda memenuhi. Tergantung pada iterator Anda, Anda dapat memilih untuk mengkhususkan std::next, std::prev, std::advance, dan std::distancejuga, tapi ini jarang diperlukan. Dalam kasus yang sangat jarang Anda mungkin ingin mengkhususkan std::begindan std::end.

Wadah Anda mungkin juga harus memiliki const_iterator, yang merupakan iterator (mungkin bisa diubah) untuk data konstan yang serupa dengan Anda, iteratorkecuali itu harus dibangun secara implisit dari a iteratordan pengguna tidak dapat memodifikasi data. Adalah umum untuk pointer internal untuk menjadi pointer ke data yang tidak konstan, dan telah iteratordiwarisi const_iteratoruntuk meminimalkan duplikasi kode.

Posting saya di Menulis Wadah Anda sendiri memiliki prototipe wadah / iterator yang lebih lengkap.

Mooing Duck
sumber
2
Selain mengkhususkan std::iterator_traitsatau menentukan sendiri typedefs, Anda juga bisa hanya berasal dari std::iterator, yang mendefinisikannya untuk Anda, tergantung pada parameter templatnya.
Christian Rau
3
@LokiAstari: Dokumentasi lengkapnya cukup luas (halaman 40ish dalam konsep), dan tidak dalam lingkup Stack Overflow. Namun, saya menambahkan lebih banyak info yang merinci tag iterator dan const_iterator. Apa lagi yang kurang dari posting saya? Anda tampaknya menyiratkan ada lebih banyak yang ditambahkan ke kelas, tetapi pertanyaannya adalah secara khusus tentang mengimplementasikan iterator.
Mooing Duck
5
std::iteratoritu diusulkan untuk usang dalam C ++ 17 ; tidak, tapi saya tidak akan menyimpannya di sekitar untuk waktu yang lama.
einpoklum
2
Pembaruan komentar @ einpoklum: std::iteratorsudah tidak digunakan lagi.
coba
1
@ JonathanLee: Wow, itu operator boolsangat berbahaya. Seseorang akan mencoba menggunakannya untuk mendeteksi akhir rentang while(it++), tetapi yang benar-benar diperiksa adalah apakah iterator dibangun dengan parameter.
Mooing Duck
16

The dokumentasi iterator_facade dari Boost.Iterator memberikan apa yang tampak seperti tutorial yang bagus pada pelaksanaan iterator untuk linked list. Bisakah Anda menggunakannya sebagai titik awal untuk membangun iterator akses-acak di atas wadah Anda?

Jika tidak ada yang lain, Anda dapat melihat fungsi anggota dan mengetik yang disediakan oleh iterator_facadedan menggunakannya sebagai titik awal untuk membangun Anda sendiri.

Michael Kristofik
sumber
10

Berikut adalah contoh iterator penunjuk mentah.

Anda seharusnya tidak menggunakan kelas iterator untuk bekerja dengan pointer mentah!

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

Solusi lingkaran berbasis rentang pointer mentah. Tolong, koreksi saya, jika ada cara yang lebih baik untuk membuat loop berbasis rentang dari pointer mentah.

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

Dan tes sederhana

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);
    }
}
Valdemar_Rudolfovich
sumber
5

Pertama-tama, Anda dapat mencari di sini daftar berbagai operasi yang perlu didukung jenis iterator individual.

Selanjutnya, ketika Anda telah membuat kelas iterator Anda, Anda perlu mengkhususkan diri std::iterator_traitsuntuk itu dan memberikan beberapa yang diperlukan typedef(seperti iterator_categoryatau value_type) atau sebagai alternatif std::iterator, yang mendefinisikan yang diperlukan typedefuntuk Anda dan karena itu dapat digunakan dengan default std::iterator_traits.

Penafian: Saya tahu beberapa orang tidak begitu suka cplusplus.com, tetapi mereka memberikan beberapa informasi yang sangat berguna tentang ini.

Christian Rau
sumber
Saya benar-benar tidak mendapatkan pertikaian cplusplus vs cppreference, keduanya bagus dan hilang banyak hal. Namun, C + + adalah satu-satunya bahasa di mana menerapkan iterator perpustakaan standar adalah XD neraka. Sering kali lebih sederhana menulis kelas pembungkus di atas wadah stl daripada menerapkan iterator XD
CoffeDeveloper
@GameDeveloper periksa pustaka templat ini yang saya tulis untuk menerapkan iterators: github.com/VinGarcia/Simple-Iterator-Template . Ini sangat sederhana dan hanya membutuhkan sekitar 10 baris kode untuk menulis iterator.
VinGarcia
Kelas yang bagus, saya menghargainya, mungkin perlu porting untuk dikompilasi juga dengan wadah non-STL (EA_STL, UE4) .. Pertimbangkan! :)
CoffeDeveloper
Lagi pula, jika satu-satunya alasan adalah bahwa cplusplus.com memberikan beberapa informasi yang sangat berguna, cppreference.com memberikan informasi yang lebih bermanfaat ...
LF
@LF Maka jangan ragu untuk kembali ke masa lalu dan tambahkan informasi itu ke versi situs 2011. ;-)
Christian Rau
3

Saya berada di kapal yang sama dengan Anda karena alasan yang berbeda (sebagian pendidikan, sebagian kendala). Saya harus menulis ulang semua wadah perpustakaan standar dan wadah harus sesuai dengan standar. Itu berarti, jika saya menukar wadah saya dengan versi stl , kodenya akan bekerja sama. Yang juga berarti saya harus menulis ulang iterators.

Bagaimanapun, saya melihat EASTL . Selain belajar banyak tentang wadah yang saya tidak pernah pelajari selama ini menggunakan wadah stl atau melalui program sarjana saya. Alasan utama adalah bahwa EASTL lebih mudah dibaca daripada rekan stl (saya menemukan ini hanya karena kurangnya semua makro dan gaya pengkodean lurus ke depan). Ada beberapa hal yang menjijikkan di sana (seperti # ifdefs untuk pengecualian) tetapi tidak ada yang membuat Anda kewalahan.

Seperti yang disebutkan orang lain, lihat referensi cplusplus.com tentang iterator dan wadah.

Samaursa
sumber
3

Saya sedang mencoba untuk memecahkan masalah untuk dapat mengulangi beberapa array teks yang berbeda yang semuanya disimpan dalam database memori yang besar struct.

Berikut ini dikerjakan menggunakan Visual Studio 2017 Community Edition pada aplikasi tes MFC. Saya memasukkan ini sebagai contoh karena posting ini adalah salah satu dari beberapa yang saya temui yang memberikan bantuan namun masih kurang untuk kebutuhan saya.

Yang structberisi data penduduk memori tampak seperti berikut ini. Saya telah menghapus sebagian besar elemen demi singkatnya dan juga belum termasuk definisi Preprocessor yang digunakan (SDK yang digunakan adalah untuk C serta C ++ dan sudah tua).

Apa yang saya tertarik lakukan adalah memiliki iterator untuk berbagai WCHARarray dua dimensi yang berisi string teks untuk mnemonik.

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;

Pendekatan saat ini adalah dengan menggunakan templat untuk menentukan kelas proxy untuk setiap array dan kemudian memiliki kelas iterator tunggal yang dapat digunakan untuk beralih pada array tertentu dengan menggunakan objek proxy yang mewakili array.

Salinan data penduduk memori disimpan dalam objek yang menangani membaca dan menulis data penduduk memori dari / ke disk. Kelas ini, CFileParaberisi kelas proksi templated ( MnemonicIteratorDimSizedan sub kelas dari mana asalnya, MnemonicIteratorDimSizeBase) dan kelas iterator MnemonicIterator,.

Objek proxy yang dibuat dilampirkan ke objek iterator yang mengakses informasi yang diperlukan melalui antarmuka yang dijelaskan oleh kelas dasar dari mana semua kelas proxy diturunkan. Hasilnya adalah memiliki satu jenis kelas iterator yang dapat digunakan dengan beberapa kelas proxy yang berbeda karena semua kelas proxy yang berbeda mengekspos antarmuka yang sama, antarmuka kelas basis proxy.

Hal pertama adalah membuat seperangkat pengidentifikasi yang akan diberikan kepada pabrik kelas untuk menghasilkan objek proxy spesifik untuk jenis mnemonik tersebut. Pengidentifikasi ini digunakan sebagai bagian dari antarmuka pengguna untuk mengidentifikasi data penyediaan tertentu yang ingin dilihat pengguna dan mungkin dimodifikasi.

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;

Kelas Proksi

Kelas proxy templated dan kelas dasarnya adalah sebagai berikut. Saya perlu mengakomodasi beberapa jenis wchar_tarray string teks. Array dua dimensi memiliki jumlah mnemonik yang berbeda, tergantung pada jenis (tujuan) mnemonik dan tipe mnemonik yang berbeda memiliki panjang maksimum yang berbeda, bervariasi antara lima karakter teks dan dua puluh karakter teks. Templat untuk kelas proksi turunan sangat cocok dengan templat yang membutuhkan jumlah karakter maksimum di setiap mnemonik. Setelah objek proxy dibuat, kami kemudian menggunakan SetRange()metode untuk menentukan array mnemonic aktual dan jangkauannya.

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

};

Kelas Iterator

Kelas iterator itu sendiri adalah sebagai berikut. Kelas ini hanya menyediakan fungsionalitas iterator maju dasar yang diperlukan saat ini. Namun saya berharap ini akan berubah atau diperpanjang ketika saya membutuhkan sesuatu tambahan darinya.

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

Pabrik objek proxy menentukan objek mana yang akan dibuat berdasarkan pengidentifikasi mnemonik. Objek proxy dibuat dan penunjuk yang dikembalikan adalah tipe kelas dasar standar sehingga memiliki antarmuka yang seragam terlepas dari bagian mnemonik mana yang sedang diakses. The SetRange()metode yang digunakan untuk menentukan ke objek proxy elemen array tertentu proxy mewakili dan berbagai elemen array.

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

Menggunakan Kelas Proxy dan Iterator

Kelas proxy dan iteratornya digunakan seperti yang ditunjukkan pada loop berikut untuk mengisi CListCtrlobjek dengan daftar mnemonik. Saya menggunakan std::unique_ptrsehingga ketika kelas proxy saya tidak lagi diperlukan dan std::unique_ptrkeluar dari ruang lingkup, memori akan dibersihkan.

Apa yang dilakukan kode sumber ini adalah membuat objek proxy untuk array di dalamnya structyang sesuai dengan pengidentifikasi mnemonik yang ditentukan. Itu kemudian menciptakan iterator untuk objek itu, menggunakan rentang foruntuk mengisi CListCtrlkontrol dan kemudian membersihkan. Ini semua adalah wchar_tstring teks mentah yang mungkin persis jumlah elemen array sehingga kami menyalin string ke buffer sementara untuk memastikan bahwa teks tersebut dihentikan nol.

    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++;
    }
Richard Chambers
sumber
1

Dan sekarang iterator kunci untuk rentang berbasis untuk loop.

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

Pemakaian:

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

Itu yang saya cari. Tapi sepertinya tidak ada yang memilikinya.

Anda mendapatkan keselarasan kode OCD saya sebagai bonus.

Sebagai latihan, tulis untuk Anda sendiri values(my_map)

Gabriel
sumber