如何正确实现自定义迭代器和const_iterators?

ere*_*eOn 217 c++ iterator const-iterator

我有一个自定义容器类,我想写它iteratorconst_iterator类.

我之前从未这样做过,但我找不到合适的方法.关于迭代器创建的指导原则是什么,我应该注意什么?

我也想避免代码重复(我觉得const_iteratoriterator分享很多东西;应该是另一个子类吗?).

脚注:我很确定Boost有什么可以缓解的,但我不能在这里使用它,因为很多愚蠢的原因.

And*_*rey 148

  • 选择适合您容器的迭代器类型:输入,输出,转发等.
  • 使用标准库中的基础迭代器类.例如,std::iterator使用random_access_iterator_tag.这些基类定义STL所需的所有类型定义并执行其他工作.
  • 为了避免代码重复,迭代器类应该是一个模板类,并通过"值类型","指针类型","引用类型"或它们全部(取决于实现)进行参数化.例如:

    // iterator class is parametrized by pointer type
    template <typename PointerType> class MyIterator {
        // iterator class definition goes here
    };
    
    typedef MyIterator<int*> iterator_type;
    typedef MyIterator<const int*> const_iterator_type;
    
    Run Code Online (Sandbox Code Playgroud)

    注意iterator_typeconst_iterator_type类型定义:它们是非const和const迭代器的类型.

另请参见:标准库参考

  • `std :: iterator`是[建议在C++ 17中弃用](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0174r0.html#2.1). (30认同)
  • 如果这已被弃用,那么正确的“新”方法是什么? (21认同)
  • `std :: iterator` [已被弃用](http://en.cppreference.com/w/cpp/iterator/iterator) (18认同)
  • @Potatoswatter:没有低估这个,但是,嘿,`random_access_iterator`不在标准中,答案不处理变量到const转换.您可能希望继承自,例如`std :: iterator <random_access_iterator_tag,value_type,...可选参数...>`. (8认同)
  • 是的,我不太确定这是如何工作的.如果我有方法`RefType operator*(){...}`,我更接近一步 - 但它没有帮助,因为我仍然需要`RefType运算符*()const {...}` . (2认同)
  • @SasQ您只需自己定义成员类型(这不是太多额外的工作) - 或者如果您喜欢的话,可以创建一个与“std::iterator”类似的模板。 (2认同)

Enz*_*nzo 49

我将向您展示如何轻松地为自定义容器定义迭代器,但以防万一我创建了一个c ++ 11库,它允许您轻松地为任何类型的容器创建自定义迭代器,连续或非constiguous.

您可以在https://github.com/navyenzo/blIteratorAPI上的github上找到它

以下是创建和使用自定义迭代器的简单步骤:

  1. 创建"自定义迭代器"类.
  2. 在"自定义容器"类中定义typedef.
    • 例如: typedef blRawIterator< Type > iterator;
    • 例如: typedef blRawIterator< const Type > const_iterator;
  3. 定义"开始""结束"功能
    • 例如: iterator begin(){return iterator(&m_data[0]);};
    • 例如: const_iterator cbegin()const{return const_iterator(&m_data[0]);};
  4. 我们完成了!

最后,定义我们的自定义迭代器类:

注意: 在定义自定义迭代器时,我们从标准迭代器类别派生,让STL算法知道我们制作的迭代器的类型

在这个例子中,我定义了一个随机访问迭代器和一个反向随机访问迭代器:

1.

//-------------------------------------------------------------------
// Raw iterator with random access
//-------------------------------------------------------------------
template<typename blDataType>
class blRawIterator
{
public:

    using iterator_category = std::random_access_iterator_tag;
    using value_type = blDataType;
    using difference_type = ptrdiff_t;
    using pointer = blDataType*;
    using reference = blDataType&;

public:

    blRawIterator(blDataType* ptr = nullptr){m_ptr = ptr;}
    blRawIterator(const blRawIterator<blDataType>& rawIterator) = default;
    ~blRawIterator(){}

    blRawIterator<blDataType>&                  operator=(const blRawIterator<blDataType>& rawIterator) = default;
    blRawIterator<blDataType>&                  operator=(blDataType* ptr){m_ptr = ptr;return (*this);}

    operator                                    bool()const
    {
        if(m_ptr)
            return true;
        else
            return false;
    }

    bool                                        operator==(const blRawIterator<blDataType>& rawIterator)const{return (m_ptr == rawIterator.getConstPtr());}
    bool                                        operator!=(const blRawIterator<blDataType>& rawIterator)const{return (m_ptr != rawIterator.getConstPtr());}

    blRawIterator<blDataType>&                  operator+=(const ptrdiff_t& movement){m_ptr += movement;return (*this);}
    blRawIterator<blDataType>&                  operator-=(const ptrdiff_t& movement){m_ptr -= movement;return (*this);}
    blRawIterator<blDataType>&                  operator++(){++m_ptr;return (*this);}
    blRawIterator<blDataType>&                  operator--(){--m_ptr;return (*this);}
    blRawIterator<blDataType>                   operator++(ptrdiff_t){auto temp(*this);++m_ptr;return temp;}
    blRawIterator<blDataType>                   operator--(ptrdiff_t){auto temp(*this);--m_ptr;return temp;}
    blRawIterator<blDataType>                   operator+(const ptrdiff_t& movement){auto oldPtr = m_ptr;m_ptr+=movement;auto temp(*this);m_ptr = oldPtr;return temp;}
    blRawIterator<blDataType>                   operator-(const ptrdiff_t& movement){auto oldPtr = m_ptr;m_ptr-=movement;auto temp(*this);m_ptr = oldPtr;return temp;}

    ptrdiff_t                                   operator-(const blRawIterator<blDataType>& rawIterator){return std::distance(rawIterator.getPtr(),this->getPtr());}

    blDataType&                                 operator*(){return *m_ptr;}
    const blDataType&                           operator*()const{return *m_ptr;}
    blDataType*                                 operator->(){return m_ptr;}

    blDataType*                                 getPtr()const{return m_ptr;}
    const blDataType*                           getConstPtr()const{return m_ptr;}

protected:

    blDataType*                                 m_ptr;
};
//-------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

2.

//-------------------------------------------------------------------
// Raw reverse iterator with random access
//-------------------------------------------------------------------
template<typename blDataType>
class blRawReverseIterator : public blRawIterator<blDataType>
{
public:

    blRawReverseIterator(blDataType* ptr = nullptr):blRawIterator<blDataType>(ptr){}
    blRawReverseIterator(const blRawIterator<blDataType>& rawIterator){this->m_ptr = rawIterator.getPtr();}
    blRawReverseIterator(const blRawReverseIterator<blDataType>& rawReverseIterator) = default;
    ~blRawReverseIterator(){}

    blRawReverseIterator<blDataType>&           operator=(const blRawReverseIterator<blDataType>& rawReverseIterator) = default;
    blRawReverseIterator<blDataType>&           operator=(const blRawIterator<blDataType>& rawIterator){this->m_ptr = rawIterator.getPtr();return (*this);}
    blRawReverseIterator<blDataType>&           operator=(blDataType* ptr){this->setPtr(ptr);return (*this);}

    blRawReverseIterator<blDataType>&           operator+=(const ptrdiff_t& movement){this->m_ptr -= movement;return (*this);}
    blRawReverseIterator<blDataType>&           operator-=(const ptrdiff_t& movement){this->m_ptr += movement;return (*this);}
    blRawReverseIterator<blDataType>&           operator++(){--this->m_ptr;return (*this);}
    blRawReverseIterator<blDataType>&           operator--(){++this->m_ptr;return (*this);}
    blRawReverseIterator<blDataType>            operator++(ptrdiff_t){auto temp(*this);--this->m_ptr;return temp;}
    blRawReverseIterator<blDataType>            operator--(ptrdiff_t){auto temp(*this);++this->m_ptr;return temp;}
    blRawReverseIterator<blDataType>            operator+(const int& movement){auto oldPtr = this->m_ptr;this->m_ptr-=movement;auto temp(*this);this->m_ptr = oldPtr;return temp;}
    blRawReverseIterator<blDataType>            operator-(const int& movement){auto oldPtr = this->m_ptr;this->m_ptr+=movement;auto temp(*this);this->m_ptr = oldPtr;return temp;}

    ptrdiff_t                                   operator-(const blRawReverseIterator<blDataType>& rawReverseIterator){return std::distance(this->getPtr(),rawReverseIterator.getPtr());}

    blRawIterator<blDataType>                   base(){blRawIterator<blDataType> forwardIterator(this->m_ptr); ++forwardIterator; return forwardIterator;}
};
//-------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

现在在您的自定义容器类中的某个位置:

template<typename blDataType>
class blCustomContainer
{
public: // The typedefs

    typedef blRawIterator<blDataType>              iterator;
    typedef blRawIterator<const blDataType>        const_iterator;

    typedef blRawReverseIterator<blDataType>       reverse_iterator;
    typedef blRawReverseIterator<const blDataType> const_reverse_iterator;

                            .
                            .
                            .

public:  // The begin/end functions

    iterator                                       begin(){return iterator(&m_data[0]);}
    iterator                                       end(){return iterator(&m_data[m_size]);}

    const_iterator                                 cbegin(){return const_iterator(&m_data[0]);}
    const_iterator                                 cend(){return const_iterator(&m_data[m_size]);}

    reverse_iterator                               rbegin(){return reverse_iterator(&m_data[m_size - 1]);}
    reverse_iterator                               rend(){return reverse_iterator(&m_data[-1]);}

    const_reverse_iterator                         crbegin(){return const_reverse_iterator(&m_data[m_size - 1]);}
    const_reverse_iterator                         crend(){return const_reverse_iterator(&m_data[-1]);}

                            .
                            .
                            .
    // This is the pointer to the
    // beginning of the data
    // This allows the container
    // to either "view" data owned
    // by other containers or to
    // own its own data
    // You would implement a "create"
    // method for owning the data
    // and a "wrap" method for viewing
    // data owned by other containers

    blDataType*                                    m_data;
};
Run Code Online (Sandbox Code Playgroud)

祝好运!!!

  • 惊人的。接受的答案太高了。这太棒了。谢谢恩佐。 (2认同)
  • 反向迭代器毫无作用,因为标准库提供了反向迭代器适配器。并且您未能使迭代器类型可从 const 迭代器分配。 (2认同)

Mat*_* M. 23

Boost有一些帮助:Boost.Iterator库.

更确切地说,这个页面:boost :: iterator_adaptor.

非常有趣的是教程示例,它从头开始显示自定义类型的完整实现.

template <class Value>
class node_iter
  : public boost::iterator_adaptor<
        node_iter<Value>                // Derived
      , Value*                          // Base
      , boost::use_default              // Value
      , boost::forward_traversal_tag    // CategoryOrTraversal
    >
{
 private:
    struct enabler {};  // a private type avoids misuse

 public:
    node_iter()
      : node_iter::iterator_adaptor_(0) {}

    explicit node_iter(Value* p)
      : node_iter::iterator_adaptor_(p) {}

    // iterator convertible to const_iterator, not vice-versa
    template <class OtherValue>
    node_iter(
        node_iter<OtherValue> const& other
      , typename boost::enable_if<
            boost::is_convertible<OtherValue*,Value*>
          , enabler
        >::type = enabler()
    )
      : node_iter::iterator_adaptor_(other.base()) {}

 private:
    friend class boost::iterator_core_access;
    void increment() { this->base_reference() = this->base()->next(); }
};
Run Code Online (Sandbox Code Playgroud)

正如已经引用的那样,要点是使用单个模板实现typedef.


Max*_*kin 22

他们经常忘记iterator必须转换为const_iterator但不是相反.这是一种方法:

template<class T, class Tag = void>
class IntrusiveSlistIterator
   : public std::iterator<std::forward_iterator_tag, T>
{
    typedef SlistNode<Tag> Node;
    Node* node_;

public:
    IntrusiveSlistIterator(Node* node);

    T& operator*() const;
    T* operator->() const;

    IntrusiveSlistIterator& operator++();
    IntrusiveSlistIterator operator++(int);

    friend bool operator==(IntrusiveSlistIterator a, IntrusiveSlistIterator b);
    friend bool operator!=(IntrusiveSlistIterator a, IntrusiveSlistIterator b);

    // one way conversion: iterator -> const_iterator
    operator IntrusiveSlistIterator<T const, Tag>() const;
};
Run Code Online (Sandbox Code Playgroud)

在上面的通知中如何IntrusiveSlistIterator<T>转换为IntrusiveSlistIterator<T const>.如果T已经是const这种转换永远不会被使用.


Pot*_*ter 16

我不知道Boost是否有任何有用的东西.

我首选的模式很简单:取一个等于value_type或不符合const 的模板参数.如有必要,还可以是节点类型.然后,好吧,一切都落到了位置.

只需记住参数化(模板化)所有需要的东西,包括复制构造函数和operator==.在大多数情况下,语义const将创建正确的行为.

template< class ValueType, class NodeType >
struct my_iterator
 : std::iterator< std::bidirectional_iterator_tag, T > {
    ValueType &operator*() { return cur->payload; }

    template< class VT2, class NT2 >
    friend bool operator==
        ( my_iterator const &lhs, my_iterator< VT2, NT2 > const &rhs );

    // etc.

private:
    NodeType *cur;

    friend class my_container;
    my_iterator( NodeType * ); // private constructor for begin, end
};

typedef my_iterator< T, my_node< T > > iterator;
typedef my_iterator< T const, my_node< T > const > const_iterator;
Run Code Online (Sandbox Code Playgroud)


Vin*_*cia 9

有很多好的答案,但是我创建了一个模板模板,它很简洁易用。

要将迭代器添加到类中,只需编写一个小类来表示具有7个小函数的迭代器状态,其中两个小函数是可选的:

#include <iostream>
#include <vector>
#include "iterator_tpl.h"

struct myClass {
  std::vector<float> vec;

  // Add some sane typedefs for STL compliance:
  STL_TYPEDEFS(float);

  struct it_state {
    int pos;
    inline void begin(const myClass* ref) { pos = 0; }
    inline void next(const myClass* ref) { ++pos; }
    inline void end(const myClass* ref) { pos = ref->vec.size(); }
    inline float& get(myClass* ref) { return ref->vec[pos]; }
    inline bool cmp(const it_state& s) const { return pos != s.pos; }

    // Optional to allow operator--() and reverse iterators:
    inline void prev(const myClass* ref) { --pos; }
    // Optional to allow `const_iterator`:
    inline const float& get(const myClass* ref) const { return ref->vec[pos]; }
  };
  // Declare typedef ... iterator;, begin() and end() functions:
  SETUP_ITERATORS(myClass, float&, it_state);
  // Declare typedef ... reverse_iterator;, rbegin() and rend() functions:
  SETUP_REVERSE_ITERATORS(myClass, float&, it_state);
};
Run Code Online (Sandbox Code Playgroud)

然后,您可以按照STL迭代器的期望使用它:

int main() {
  myClass c1;
  c1.vec.push_back(1.0);
  c1.vec.push_back(2.0);
  c1.vec.push_back(3.0);

  std::cout << "iterator:" << std::endl;
  for (float& val : c1) {
    std::cout << val << " "; // 1.0 2.0 3.0
  }

  std::cout << "reverse iterator:" << std::endl;
  for (auto it = c1.rbegin(); it != c1.rend(); ++it) {
    std::cout << *it << " "; // 3.0 2.0 1.0
  }
}
Run Code Online (Sandbox Code Playgroud)

希望对您有所帮助。


Kei*_*van 5

我看到这篇文章,很惊讶这里没有真正提到一个简单的方法。使用像std::iterator所描述的那样指向值的指针显然是一种非常通用的方法。但你也许可以做一些更简单的事情。当然,这是一种简单化的方法,可能并不总是足够的,但如果是的话,我会将其发布给下一位读者。

您的类中的基础类型很可能是一个 STL 容器,它已经为您定义了迭代器。如果是这种情况,您可以简单地使用它们定义的迭代器,而不必真正创建自己的迭代器。

这是一个例子:

class Foo {

  std::vector<int>::iterator begin() { return data.begin(); }
  std::vector<int>::iterator end() { return data.end(); }

  std::vector<int>::const_iterator begin() const { return data.begin(); }
  std::vector<int>::const_iterator end() const { return data.end(); }


private:
  std::vector<int> data

};
Run Code Online (Sandbox Code Playgroud)


Tre*_*ard 5

我很想知道这有多正确,但似乎可以作为内部数据存储的滚动迭代器

template<typename T>
struct iterator_type
{
    using self_type             = iterator_type;
    using iterator_category     = std::random_access_iterator_tag;
    using difference_type       = std::ptrdiff_t;
    using value_type            = std::remove_cv_t<T>;
    using pointer               = T*;
    using reference             = T&;

    iterator_type( pointer ptr ) noexcept
        : _ptr{ ptr }
    {}

    reference operator*() noexcept { return *_ptr; }
    pointer operator->() noexcept { return _ptr; }

    self_type operator++() noexcept { ++_ptr; return *this; }
    self_type operator++(int) noexcept { self_type tmp = *this; ++_ptr; return tmp; }

    self_type operator--() noexcept { --_ptr; return *this; }
    self_type operator--(int) noexcept { self_type tmp = *this; --_ptr; return tmp; }

    bool operator==( const self_type &other ) const noexcept { return _ptr == other._ptr; }
    bool operator!=( const self_type &other ) const noexcept { return _ptr != other._ptr; }

private:
    pointer _ptr;
};


template<typename T>
using const_iterator_type = iterator_type<std::add_const_t<T>>;
Run Code Online (Sandbox Code Playgroud)

然后我将这些添加到我的类中,并且似乎按预期工作。

template<typename T>
class Container
{
public:
    using iterator               = iterator_type<T>;
    using const_iterator         = const_iterator_type<T>;
    using reverse_iterator       = std::reverse_iterator<iterator>;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;

...

    iterator begin() { return _begin; }
    iterator end() { return _begin + _size; }

    const_iterator cbegin() const { return _begin; }
    const_iterator cend() const { return _begin + _size; }

    reverse_iterator rbegin() { return reverse_iterator(_begin + _size); }
    reverse_iterator rend() { return reverse_iterator(_begin); }

    const_reverse_iterator crbegin() const { return const_reverse_iterator(_begin + _size); }
    const_reverse_iterator crend() const { return const_reverse_iterator(_begin); }

private:
    T*         _begin;
    size_t     _size;
    size_t     _capacity;
};
Run Code Online (Sandbox Code Playgroud)

唯一的事情是,要使用std::cbegin()std::rcbegin()和函数来实现它std::cend()std::rcend()我必须扩展std命名空间:

namespace std
{
    template<typename T>
    typename Container<T>::const_iterator cbegin( Container<T> &c ) { return c.cbegin(); }

    template<typename T>
    typename Container<T>::const_iterator cend( Container<T> &c ) { return c.cend(); }

    template<typename T>
    typename Container<T>::const_reverse_iterator crbegin( Container<T> &c ) { return c.crbegin(); }

    template<typename T>
    typename Container<T>::const_reverse_iterator crend( Container<T> &c ) { return c.crend(); }
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

167512 次

最近记录:

6 年,2 月 前