我有一个自定义容器类,我想为其编写iterator
和const_iterator
类。
我以前从未做过,但是找不到合适的方法。关于迭代器创建的准则是什么,我应该注意什么?
我还想避免代码重复(我感觉到const_iterator
并iterator
共享许多东西;一个应该继承另一个吗?)。
脚注:我很确定Boost可以缓解此问题,但是由于许多愚蠢的原因,我不能在这里使用它。
我有一个自定义容器类,我想为其编写iterator
和const_iterator
类。
我以前从未做过,但是找不到合适的方法。关于迭代器创建的准则是什么,我应该注意什么?
我还想避免代码重复(我感觉到const_iterator
并iterator
共享许多东西;一个应该继承另一个吗?)。
脚注:我很确定Boost可以缓解此问题,但是由于许多愚蠢的原因,我不能在这里使用它。
Answers:
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;
注意iterator_type
和const_iterator_type
类型定义:它们是您的非const和const迭代器的类型。
另请参见:标准库参考
编辑: std::iterator
自C ++ 17起不推荐使用。请参阅此处的相关讨论。
random_access_iterator
这不在标准之内,答案无法处理可变的const转换。您可能想要继承,例如std::iterator<random_access_iterator_tag, value_type, ... optional arguments ...>
。
RefType operator*() { ... }
,我将更近一步-但这没有帮助,因为我仍然需要RefType operator*() const { ... }
。
std::iterator
被提议用于C ++ 17弃用。
我将向您展示如何轻松地为自定义容器定义迭代器,但以防万一我创建了一个c ++ 11库,该库可让您轻松地针对任何类型的容器(相邻或连续)创建具有自定义行为的自定义迭代器非连续的。
你可以在Github上找到它
以下是创建和使用自定义迭代器的简单步骤:
typedef blRawIterator< Type > iterator;
typedef blRawIterator< const Type > const_iterator;
iterator begin(){return iterator(&m_data[0]);};
const_iterator cbegin()const{return const_iterator(&m_data[0]);};
最后,定义我们的自定义迭代器类:
注意: 在定义自定义迭代器时,我们从标准迭代器类别中派生出来,以使STL算法知道我们制作的迭代器的类型。
在此示例中,我定义了一个随机访问迭代器和一个反向随机访问迭代器:
//-------------------------------------------------------------------
// 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 = std::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 difference_type& movement){m_ptr += movement;return (*this);}
blRawIterator<blDataType>& operator-=(const difference_type& movement){m_ptr -= movement;return (*this);}
blRawIterator<blDataType>& operator++(){++m_ptr;return (*this);}
blRawIterator<blDataType>& operator--(){--m_ptr;return (*this);}
blRawIterator<blDataType> operator++(int){auto temp(*this);++m_ptr;return temp;}
blRawIterator<blDataType> operator--(int){auto temp(*this);--m_ptr;return temp;}
blRawIterator<blDataType> operator+(const difference_type& movement){auto oldPtr = m_ptr;m_ptr+=movement;auto temp(*this);m_ptr = oldPtr;return temp;}
blRawIterator<blDataType> operator-(const difference_type& movement){auto oldPtr = m_ptr;m_ptr-=movement;auto temp(*this);m_ptr = oldPtr;return temp;}
difference_type 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;
};
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// 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 difference_type& movement){this->m_ptr -= movement;return (*this);}
blRawReverseIterator<blDataType>& operator-=(const difference_type& 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++(int){auto temp(*this);--this->m_ptr;return temp;}
blRawReverseIterator<blDataType> operator--(int){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;}
difference_type operator-(const blRawReverseIterator<blDataType>& rawReverseIterator){return std::distance(this->getPtr(),rawReverseIterator.getPtr());}
blRawIterator<blDataType> base(){blRawIterator<blDataType> forwardIterator(this->m_ptr); ++forwardIterator; return forwardIterator;}
};
//-------------------------------------------------------------------
现在在您的自定义容器类中的某个位置:
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;
};
m_data[m_size]
is UB。您只需将其替换为即可对其进行修复m_data+m_size
。对于反向迭代器,m_data[-1]
和m_data-1
都不正确(UB)。要修复reverse_iterators,您将需要使用“下一个元素技巧的指针”。
他们常常忘记,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;
};
在上述通知中,如何IntrusiveSlistIterator<T>
转换为IntrusiveSlistIterator<T const>
。如果T
已经存在,则const
此转换永远不会被使用。
const
为non- ,则它将无法编译const
。
IntrusiveSlistIterator<T const, void>::operator IntrusiveSlistIterator<T const, void>() const
吗?
enable_if
可能解决它,但是...
Boost有一些帮助:Boost.Iterator库。
更准确地说,此页面是:boost :: iterator_adaptor。
非常有趣的是Tutorial Example,从头开始显示了自定义类型的完整实现。
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(); } };
正如已经提到的,要点是使用单个模板实现及其typedef
。
// a private type avoids misuse
enabler
永远都不打算由调用者提供,所以我的猜测是他们将其设为私有,以避免人们意外地尝试通过它。我认为,由于保护位于,因此它不会产生任何问题来实际通过它enable_if
。
我不知道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;
cur
从相反的constness的迭代器访问可能会出现问题。friend my_container::const_iterator; friend my_container::iterator;
我想到的解决方案是,但是我不认为这是我以前做过的……总之,总的说来可行。
friend class
在两种情况下都要这样做。
有很多好的答案,但是我创建了一个模板头,非常简洁且易于使用。
要将迭代器添加到您的类,只需编写一个小类来表示具有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);
};
然后,您可以按照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
}
}
希望对您有所帮助。