STL 스타일 반복자를 구현하고 일반적인 함정을 피하는 방법은 무엇입니까?


306

STL 스타일의 랜덤 액세스 반복자를 제공하려는 컬렉션을 만들었습니다. 반복자의 구현 예제를 찾고 있었지만 찾지 못했습니다. const 과부하 []*연산자 의 필요성에 대해 알고 있습니다. 반복자가 "STL 스타일"이되기위한 요구 사항은 무엇이며 피해야 할 다른 함정은 무엇입니까?

추가 컨텍스트 : 이것은 라이브러리 용이며 실제로 필요한 경우가 아니라면 라이브러리에 대한 종속성을 도입하고 싶지 않습니다. 동일한 컴파일러로 C ++ 03과 C ++ 11 간의 이진 호환성을 제공 할 수 있도록 내 컬렉션을 작성합니다 (따라서 STL이 깨질 수 있습니다).


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 표준의 § 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>하거나 반복자 자체에 동일한 typedef를 넣거나 상속 할 수 있습니다 std::iterator(이러한 typedef가 있음). 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::beginstd::end.

컨테이너에는 아마도.이 있어야합니다 const_iterator. 이것은 iterator암시 적으로 암시 적으로 구성 iterator할 수 있어야하고 사용자가 데이터를 수정할 수 없다는 점을 제외하고는 귀하와 비슷한 상수 데이터에 대한 (가능하게 변경 가능한) 반복자 입니다. 내부 포인터가 일정하지 않은 데이터에 대한 포인터 인 것이 일반적이며, 코드 중복을 최소화하기 위해 iterator상속 const_iterator합니다.

STL 컨테이너 작성에 대한 내 게시물 에는 더 완벽한 컨테이너 / 반복기 프로토 타입이 있습니다.


2
std::iterator_traitstypedef를 직접 정의하거나 정의하는 것 외에도 std::iterator템플릿 매개 변수에 따라을 파생시킬 수 있습니다 .
Christian Rau

3
@LokiAstari : 전체 문서는 스택 오버플로의 범위가 아닌 상당히 광범위합니다 (초안의 40 페이지). 그러나 반복자 태그 및에 대한 자세한 정보를 추가했습니다 const_iterator. 내 게시물에 무엇이 부족 했습니까? 클래스에 추가 할 것이 더 있음을 암시하는 것처럼 보이지만 문제는 반복자를 구현하는 것에 관한 것입니다.
Mooing Duck

5
std::iteratorC ++ 17에서 더 이상 사용되지 않는 것으로 제안되었습니다 . 그렇지는 않았지만 훨씬 더 오랫동안 주변에 있지는 않았습니다.
einpoklum

2
@einpoklum의 의견에 대한 업데이트 : std::iterator결국 폐기되었습니다.
scry

1
@JonathanLee : 와우, 그것은 operator bool엄청나게 위험합니다. 누군가 그것을 사용하여 범위의 끝을 감지하려고 시도 while(it++)하지만 실제로 확인하는 것은 반복기가 매개 변수로 구성되었는지 여부입니다.
Mooing Duck

16

Boost.Iterator 의 iterator_facade 문서 는 링크 된 목록에 대한 반복자를 구현하는 방법에 대한 훌륭한 튜토리얼을 제공합니다. 컨테이너에 랜덤 액세스 반복자를 빌드하기위한 시작점으로 사용할 수 있습니까?

다른 것이 없다면, 제공 한 멤버 함수와 typedef를 살펴보고 iterator_facade자신 만의 고유 한 시작점으로 사용할 수 있습니다.



10

다음은 raw pointer iterator 샘플입니다.

raw 포인터로 작업하기 위해 iterator 클래스를 사용해서는 안됩니다!

#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않지만 이것에 대한 유용한 정보를 제공합니다.


나는 실제로 cplusplus 대 cppreference 분쟁을 얻지 못합니다. 그러나 C ++은 표준 라이브러리 반복자를 구현하는 것이 지옥 XD 인 유일한 언어입니다. 대부분 iterator XD를 구현하는 것보다 stl 컨테이너를 통해 래퍼 클래스를 작성하는 것이 더 간단합니다.
CoffeDeveloper

: 나는 반복자를 구현하기위한 쓴이 템플릿 라이브러리 확인 @GameDeveloper github.com/VinGarcia/Simple-Iterator-Template을 . 매우 간단하며 반복자를 작성하려면 약 10 줄의 코드 만 필요합니다.
VinGarcia

좋은 수업, 감사합니다. 아마도 STL이 아닌 컨테이너 (EA_STL, UE4)로 컴파일 할 가치가 있습니다. 고려해보십시오! :)
CoffeDeveloper 2016

어쨌든, 유일한 이유가 cplusplus.com이 실제로 유용한 정보를 제공하는 것이라면 cppreference.com은보다 유용한 정보를 제공합니다.
LF

@LF 그런 다음 시간을 거슬러 올라가 해당 정보를 2011 버전의 사이트에 추가하십시오. ;-)
Christian Rau

3

나는 다른 이유로 (일부 교육적, 부분적 제약) 당신과 같은 보트에 있었고 / 오전입니다. 표준 라이브러리의 모든 컨테이너를 다시 작성해야했고 컨테이너는 표준을 준수해야했습니다. 즉, 컨테이너를 stl 버전으로 교체 하면 코드가 동일하게 작동합니다. 또한 반복자를 다시 작성해야 함을 의미했습니다.

어쨌든, 나는 EASTL을 보았다 . 컨테이너에 대한 톤을 배우는 것 외에도 stl 컨테이너를 사용하거나 학부 과정을 통해 이번에 배운 적이 없습니다 . 주된 이유는 EASTLstl에 비해 더 읽기 쉽기 때문입니다 (이것은 모든 매크로가 부족하고 코딩 방식이 간단하기 때문입니다). #ifdefs와 같은 예외적 인 것들이 있지만 압도 할 것은 없습니다.

다른 사람들이 언급했듯이 반복자와 컨테이너에 대한 cplusplus.com의 참조를보십시오.


3

나는 큰 메모리 상주 데이터베이스 내에 저장된 여러 다른 텍스트 배열을 반복 할 수있는 문제를 해결하려고했다 struct.

다음은 MFC 테스트 응용 프로그램에서 Visual Studio 2017 Community Edition을 사용하여 해결되었습니다. 나는이 게시물이 내가 도움을 제공했지만 여전히 내 요구에 충분하지 않은 것을 가로 질러 여러 가지 중 하나 였기 때문에 이것을 예로 들어 설명하고 있습니다.

struct포함하는 메모리 상주 데이터는 다음과 같은 것을 보았다. 간결성을 위해 대부분의 요소를 제거했으며 사용 된 전 처리기 정의도 포함하지 않았습니다 (사용중인 SDK는 C뿐만 아니라 C ++이며 오래되었습니다).

내가하고 싶은 것은 WCHAR니모닉을위한 텍스트 문자열을 포함 하는 다양한 2 차원 배열에 대한 반복자를 갖는 것입니다 .

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텍스트 문자열 배열 을 수용해야했습니다 . 2 차원 배열은 니모닉의 유형 (목적)에 따라 다른 니모닉 수를 가졌으며, 니모닉의 다른 유형은 최대 길이가 다르며 5 개의 텍스트 문자와 20 개의 텍스트 문자 사이에서 다양합니다. 파생 된 프록시 클래스의 템플리트는 각 니모닉에서 최대 문자 수를 요구하는 템플리트와 자연스럽게 맞았습니다. 프록시 객체가 생성 된 후 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
};

프록시 객체 팩토리는 니모닉 식별자를 기반으로 생성 할 객체를 결정합니다. 프록시 오브젝트가 작성되고 리턴되는 포인터는 표준 니즈 닉 섹션 중 어느 것이 액세스되는지에 상관없이 균일 한 인터페이스를 갖도록 표준 기본 클래스 유형입니다. 이 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하는 것은 지정된 니모닉 식별자에 해당하는 배열에 대한 프록시 객체를 만드는 것 입니다. 그런 다음 해당 개체의 반복자를 만들고 범위 for를 사용하여 CListCtrl컨트롤 을 채우고 정리합니다. 이들은 모두 wchar_t배열 요소의 수일 수있는 모든 원시 텍스트 문자열이므로 텍스트가 0으로 종료되지 않도록 문자열을 임시 버퍼에 복사합니다.

    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

그리고 이제 범위 기반 for 루프의 키 반복자입니다.

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.