STL tarzı yineleyici nasıl uygulanır ve yaygın tuzaklar nasıl önlenir?


306

STL tarzı, rasgele erişimli bir yineleyici sağlamak istediğim bir koleksiyon yaptım. Bir yineleyicinin örnek bir uygulamasını araştırıyordum ama hiç bulamadım. Const aşırı yükleri ihtiyacını biliyorum []ve* operatörlerin . Bir yineleyicinin "STL-tarzı" olması için gereksinimler nelerdir ve kaçınılması gereken diğer tuzaklar nelerdir?

Ek bağlam: Bu bir kütüphane ve gerçekten gerekmedikçe ona bağımlılık getirmek istemiyorum. Ben aynı derleyici ile C ++ 03 ve C ++ 11 ikili uyumluluk sağlamak için kendi koleksiyon yazmak (muhtemelen kırmak hiçbir STL).


13
1! Güzel soru. Aynı şeyi merak ettim. Boost.Iterator'a dayalı bir şeyleri birlikte hareket ettirmek yeterince kolaydır, ancak sıfırdan uygularsanız gereksinimlerin bir listesini bulmak şaşırtıcı derecede zordur.
jalf

2
Ayrıca yineleyicilerin KORKUNMASI gerektiğini unutmayın. boost.org/doc/libs/1_55_0/doc/html/intrusive/…
alfC

Yanıtlar:


232

http://www.cplusplus.com/reference/std/iterator/ , C ++ 11 standardının § 24.2.2 teknik özelliklerini açıklayan kullanışlı bir tabloya sahiptir. Temel olarak, yineleyiciler geçerli işlemleri açıklayan etiketlere sahiptir ve etiketlerin bir hiyerarşisi vardır. Aşağıda tamamen semboliktir, bu sınıflar aslında böyle değildir.

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.

Ya uzmanlaşabilir std::iterator_traits<youriterator>ya gelen yineleyici kendisi veya devralır aynı typedefs koymak std::iterator(bu typedefs sahip olan). stdAd alanını değiştirmekten ve okunabilirlikten kaçınmak için ikinci seçeneği tercih ediyorum , ancak çoğu insan miras alıyor 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
};

Not iterator_category biri olmalıdır std::input_iterator_tag, std::output_iterator_tag, std::forward_iterator_tag, std::bidirectional_iterator_tag, veya std::random_access_iterator_tag, bağlı hangi gereksinimler yineleyici tatmin üzerinde. Senin Yineleyici bağlı olarak, uzmanlaşmak tercih edebilir std::next, std::prev, std::advance, ve std::distanceaynı zamanda, ancak bu nadiren gereklidir. Gelen son derece nadir durumlarda size uzmanlaşmak isteyebilir std::beginvestd::end .

Kapsayıcınızda büyük const_iteratorolasılıkla a'ya benzer sabit veriler için (muhtemelen değiştirilebilen) bir yineleyici iteratorde bulunmalıdır . Ancak, örtük olarak a'dan oluşturulabilir iteratorolması gerekir ve kullanıcılar verileri değiştiremez. Dahili işaretçisinin sabit olmayan verilere bir işaretçi olması ve kod çoğaltmasını en aza indirecek şekilde iteratormiras alması yaygındır const_iterator.

Kendi STL Kapsayıcısını yazmaktaki görevim daha eksiksiz bir kapsayıcı / yineleyici prototipine sahip.


2
std::iterator_traitsTypedefs'i kendiniz uzmanlaştırmaya veya tanımlamaya ek std::iteratorolarak, şablon parametrelerine bağlı olarak, bunları sizin için tanımlayan sadece türetilebilir .
Christian Rau

3
@LokiAstari: Tüm belgeler oldukça geniştir (taslakta 40ish sayfa) ve Yığın Taşması kapsamında değildir. Ancak, yineleyici etiketlerini detaylandıran daha fazla bilgi ekledim ve const_iterator. Görevimde başka neler yoktu? Sınıfa eklenecek daha çok şey olduğu anlaşılıyor, ancak soru özellikle yineleyicileri uygulamakla ilgili.
Mooing Duck

5
std::iteratoredilmiş C ++ 17 kaldırılacaktır önerilmiştir ; değildi, ama çok daha uzun süre etrafta olmasından bahsetmezdim.
einpoklum

2
@ Einpoklum'un yorumunda bir güncelleme: std::iteratorsonuçta kullanımdan kaldırıldı.
scry

1
@JonathanLee: Vay canına, bu operator boolinanılmaz derecede tehlikeli. Birisi bunu bir aralığın sonunu tespit etmek için kullanmaya çalışacaktır while(it++), ancak gerçekten kontrol ettiği tek şey, yineleyicinin bir parametre ile oluşturulmuş olup olmadığıdır.
Mooing Duck

16

iterator_facade dokümantasyon Boost.Iterator Bağlantılı bir liste için iterators uygulanmasına güzel öğretici neye benzediğini sağlar. Bunu, kabınız üzerinde rasgele erişimli bir yineleyici oluşturmak için bir başlangıç ​​noktası olarak kullanabilir misiniz?

Başka bir şey yoksa, tarafından sağlanan üye işlevlerine ve typedef'lere göz atabilir ve kendi işlevlerinizi iterator_facadeoluşturmak için bir başlangıç ​​noktası olarak kullanabilirsiniz.



10

İşte ham işaretçi yineleyici örneği.

Ham işaretçilerle çalışmak için yineleyici sınıfını kullanmamalısınız!

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

Ham işaretçi aralığı tabanlı döngü geçici çözümü. Ham işaretçiden aralık tabanlı döngü yapmanın daha iyi bir yolu varsa lütfen beni düzeltin.

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

Ve basit test

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

Her şeyden önce , tek tek yineleyici türlerinin desteklemesi gereken çeşitli işlemlerin bir listesini burada bulabilirsiniz .

Daha sonra, yineleyici sınıfınızı std::iterator_traitsoluşturduğunuzda, bunun için uzmanlaşmanız ve sizin için gerekli olanları tanımlayan ve bu nedenle varsayılan olarak kullanılabilecek bazı gerekli typedef( iterator_categoryveya gibi value_type) veya alternatif olarak türetmeniz gerekir .std::iteratortypedefstd::iterator_traits

Feragatname: Bazı insanların cplusplus.combu kadar sevmediğini biliyorum , ama bu konuda gerçekten yararlı bilgiler veriyorlar.


Gerçekten cplusplus vs cppreference anlaşmazlığı alamadım, ikisi de iyi ve birçok şeyi eksik. Bununla birlikte, C ++ standart kütüphane yineleyicilerini uygulamanın cehennem XD olduğu tek dildir. Çoğu zaman, bir stl kabı üzerine bir sarıcı sınıf yazmak, bir yineleyici XD uygulamaktan daha
kolaydır

@GameDeveloper yineleyicileri uygulamak için yazdığım bu şablon kütüphanesini kontrol edin: github.com/VinGarcia/Simple-Iterator-Template . Çok basittir ve bir yineleyici yazmak için sadece yaklaşık 10 satır kod gerektirir.
VinGarcia

Nice class, ben takdir, muhtemelen STL olmayan kaplar (EA_STL, UE4) ile de derlemek için taşıma değer .. Bunu düşünün! :)
CoffeDeveloper

Her neyse, tek neden cplusplus.com gerçekten yararlı bilgiler sağlıyorsa, cppreference.com daha faydalı bilgiler sunar ...
LF

@LF Daha sonra geri dönüp sitenin 2011 sürümüne bu bilgileri eklemekten çekinmeyin. ;-)
Christian Rau

3

Farklı nedenlerle (kısmen eğitsel, kısmen kısıtlamalar) sizinle aynı teknedeydim. Standart kütüphanenin tüm kaplarını yeniden yazmak zorunda kaldım ve kaplar standarda uymak zorundaydı. Bu, benim konteyner stl sürümü ile takas , kod aynı çalışır anlamına gelir . Bu da yineleyicileri tekrar yazmak zorunda olduğum anlamına geliyordu.

Her neyse, EASTL'ye baktım . Bunların dışında hiç bir zaman stl kapları kullanarak ya da lisans derslerim aracılığıyla öğrendiğim kapsayıcılar hakkında bir ton öğrenmek . Ana nedeni, EASTL'nin stl muadilinden daha okunabilir olmasıdır (bunun sadece makroların ve düz kodlama stilinin eksikliğinden kaynaklandığını gördüm). Orada bazı tuhaf şeyler (istisnalar için # ifdefs gibi) ama sizi bunaltacak hiçbir şey yok.

Diğerlerinin de belirttiği gibi, cplusplus.com'un yineleyiciler ve kapsayıcılar hakkındaki referansına bakın.


3

Hepsi büyük bir bellek yerleşik veritabanı içinde saklanan birkaç farklı metin dizileri üzerinde yineleme yapabilmek sorunu çözmeye çalışıyordu struct.

Aşağıdakiler bir MFC test uygulamasında Visual Studio 2017 Community Edition kullanılarak çalışıldı. Ben bu örnek bir örnek olarak dahil ediyorum çünkü bu yazı bazı yardım sağlanan ama yine de benim ihtiyaçları için hala yetersiz olduğunu koştum birkaç biri oldu.

structİçeren bellek yerleşik veri Aşağıdaki gibi bir şey görünüyordu. Ben kısaca uğruna elemanların çoğunu kaldırdım ve kullanılan Önişlemci tanımlarını da dahil etmedim (kullanılan SDK, C'nin yanı sıra C ++ içindir ve eskidir).

Yapmakla ilgilendiğim şey, WCHARanımsatıcılar için metin dizeleri içeren çeşitli iki boyutlu diziler için yineleyiciler kullanmaktır .

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;

Geçerli yaklaşım, dizilerin her biri için bir proxy sınıfı tanımlamak için bir şablon kullanmak ve daha sonra diziyi temsil eden bir proxy nesnesi kullanarak belirli bir dizi üzerinde yineleme yapmak için kullanılabilecek tek bir yineleyici sınıfına sahip olmaktır.

Bellek yerleşik verilerinin bir kopyası, bellek yerleşik verilerinin diske / diskten okunmasını ve yazılmasını sağlayan bir nesnede saklanır. Bu sınıf, CFileParageçici proxy sınıfını ( MnemonicIteratorDimSizeve türetildiği alt sınıfı MnemonicIteratorDimSizeBase) ve yineleyici sınıfını içerir MnemonicIterator.

Oluşturulan proxy nesnesi, tüm proxy sınıflarının türetildiği bir temel sınıf tarafından tanımlanan bir arabirim aracılığıyla gerekli bilgilere erişen bir yineleyici nesnesine eklenir. Sonuç, farklı proxy sınıflarının hepsi aynı arabirimi, proxy temel sınıfının arabirimini ortaya çıkardığı için birkaç farklı proxy sınıfıyla kullanılabilen tek bir yineleyici sınıfına sahip olmaktır.

İlk şey, söz konusu anımsatıcı türü için belirli proxy nesnesini oluşturmak üzere bir sınıf fabrikasına sağlanacak bir dizi tanımlayıcı oluşturmaktı. Bu tanımlayıcılar, kullanıcının görmek ve muhtemelen değiştirmek istediği belirli hazırlık verilerini tanımlamak için kullanıcı arayüzünün bir parçası olarak kullanılır.

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;

Proxy Sınıfı

Şablonlu proxy sınıfı ve temel sınıfı aşağıdaki gibidir. Birkaç farklı wchar_tmetin dizesi dizisi barındırmak gerekiyordu. İki boyutlu diziler, anımsatıcının türüne (amacına) bağlı olarak farklı sayıda anımsatıcıya sahipti ve farklı anımsatıcı türleri, beş metin karakteri ve yirmi metin karakteri arasında değişen farklı maksimum uzunluklara sahipti. Türetilmiş proxy sınıfı için şablonlar, her anımsatıcıda maksimum karakter sayısı gerektiren şablona doğal bir uyum sağladı. Proxy nesnesi oluşturulduktan sonra SetRange(), gerçek anımsatıcı diziyi ve aralığını belirtmek için yöntemi kullanırız.

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

};

Yineleyici Sınıfı

Yineleyici sınıfının kendisi aşağıdaki gibidir. Bu sınıf, şu anda ihtiyaç duyulan tek şey temel ileri yineleyici işlevselliği sağlar. Ancak, bundan başka bir şeye ihtiyacım olduğunda bunun değişeceğini veya genişletileceğini umuyorum.

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

Proxy nesne fabrikası, anımsatıcı tanımlayıcısına göre hangi nesnenin oluşturulacağını belirler. Proxy nesnesi oluşturulur ve döndürülen işaretçi, farklı anımsatıcı bölümlerden hangisine erişildiğine bakılmaksızın tek tip bir arabirime sahip olacak şekilde standart temel sınıf türüdür. SetRange()Yöntem vekil nesneye vekil temsil ettiği özel dizi elemanları ve dizi elemanlarının aralığı belirtmek için kullanılır.

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 Sınıfını ve Yineleyiciyi Kullanma

Proxy sınıfı ve yineleyicisi, bir CListCtrlnesneyi anımsatıcı listesiyle doldurmak için aşağıdaki döngüde gösterildiği gibi kullanılır . Ben kullanıyorum std::unique_ptrvekil sınıf i değil artık ihtiyaç ve ne zaman ki std::unique_ptrkapsam dışına gider, hafıza kadar temizlenecektir.

Bu kaynak kodun yaptığı, içinde structbelirtilen anımsatıcı tanımlayıcısına karşılık gelen dizi için bir proxy nesnesi oluşturmaktır . Daha sonra bu nesne için bir yineleyici oluşturur for, CListCtrlkontrolü doldurmak için bir aralık kullanır ve sonra temizler. Bunların hepsi wchar_ttam olarak dizi öğesi sayısı olabilen ham metin dizeleridir, bu nedenle metnin sıfır sonlandırıldığından emin olmak için dizeyi geçici bir ara belleğe kopyalarız.

    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

Ve şimdi döngü için aralık tabanlı bir anahtar yineleyici.

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

Kullanımı:

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

Aradığım şey buydu. Ama kimsede yoktu, öyle görünüyor.

OCD kod hizalamamı bonus olarak alıyorsunuz.

Bir egzersiz olarak, values(my_map)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.