In "Mastering the C++17 STL" book I saw both iterator and const_iterator implementation in one class using conditional for less code duplication
Here's my implementation for simple array class (most code for array class is skipped):
template<class T, size_t N>
class Array
{
public:
template<bool Const>
class ArrayIterator {
friend class Array;
public:
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = std::conditional<Const, const value_type*, value_type*>;
using reference = std::conditional<Const, const value_type&, value_type&>;
using iterator_category = std::random_access_iterator_tag;
reference operator*() const { return *ptr; }
ArrayIterator<Const>& operator++() { ++ptr; return *this; }
ArrayIterator<Const> operator++(int) { auto res = *this; ++(*this); return res; }
template<bool R>
bool operator==(const ArrayIterator<R>& iter) const { return ptr == iter.ptr; }
template<bool R>
bool operator!=(const ArrayIterator<R>& iter) const { return ptr != iter.ptr; }
private:
explicit ArrayIterator(pointer p) : ptr(p) {};
pointer ptr;
};
using iterator = ArrayIterator<false>;
using const_iterator = ArrayIterator<true>;
iterator begin() { return iterator(data); }
iterator end() { return iterator(data + N); }
const_iterator cbegin() const { return const_iterator(data); }
const_iterator cend() const { return const_iterator(data + N); }
private:
T* data;
};
This code compiles with no errors, but iterator is kinda unusable:
Array<int, 100> arr;
/*filling it with numbers*/
int x = *arr.begin();
Gives error:
main.cpp:9:9: error: no viable conversion from 'Array<int, 100>::ArrayIterator<false>::reference' (aka 'conditional<false, const int &, int &>') to 'int'
How can I use that iterator or should I just abandon this idea from book?
The member type pointer and reference of ArrayIterator should be defined as member type type of std::conditional, not std::conditional itself.
Change them to:
using pointer = typename std::conditional<Const, const value_type*, value_type*>::type;
// ^^^^^^^^ ^^^^^^
using reference = typename std::conditional<Const, const value_type&, value_type&>::type;
// ^^^^^^^^ ^^^^^^
Or (since C++14)
using pointer = std::conditional_t<Const, const value_type*, value_type*>;
// ^^
using reference = std::conditional_t<Const, const value_type&, value_type&>;
// ^^
I am trying to recreate some of the C++ containers for a school project and for that I had to also implement iterators. I am currently working on the List container and I am facing a conversion problem.
Here are the parts of the code that are involved:
I have an Elem structure (corresponding to 1 element of a doubly linked list that I use for my List container)
template <class T>
struct Elem
{
Elem *prev;
T data;
Elem *next;
};
a BidirectionalIterator class (used for the list iterators). Here are the constructors:
template <class T>
class BidirectionalIterator
{
public:
typedef BidirectionalIterator iterator;
typedef T value_type;
typedef size_t size_type;
BidirectionalIterator() { _ptr = nullptr; };
BidirectionalIterator(Elem<value_type> *ptr) {
*this->_ptr = ptr;
};
BidirectionalIterator(const iterator &x) {
*this->_ptr = x._ptr;
};
~BidirectionalIterator() {};
iterator &operator=(const iterator &x) {
*this->_ptr = x._ptr;
return (*this);
};
[...]
};
and my list class:
template <class T, class Alloc = std::allocator<T>>
class list
{
public:
typedef T value_type;
typedef BidirectionalIterator<T> iterator;
typedef BidirectionalIterator<const T> const_iterator;
typedef size_t size_type;
/* CONSTRUCTORS */
[...]
list(const list &x) {
_init_list();
assign(x.begin(), x.end());
};
/* ITERATORS */
iterator begin() {
return (iterator(_start));
};
const_iterator begin() const {
return (const_iterator(_start));
};
iterator end() {
return (iterator(_tail));
};
const_iterator end() const {
return (const_iterator(_tail));
};
/* ASSIGN */
void assign(iterator first, iterator last);
void assign(const_iterator first, const_iterator last);
[...]
private:
Elem<value_type> *_head;
Elem<value_type> *_start;
Elem<value_type> *_end;
Elem<value_type> *_tail;
[...]
};
In my main program I' m just calling a function (T being an int) that implicitely calls the copy constructor:
void print_content(ft::list<T> lst);
But when I compile i get this:
./List.hpp:71:12: error: no matching conversion for functional-style cast from 'Elem<ft::list<int, std::allocator<int>
>::value_type> *const' (aka 'Elem<int> *const') to 'ft::list<int, std::allocator<int> >::const_iterator' (aka
'BidirectionalIterator<const int>')
return (const_iterator(_start));
^~~~~~~~~~~~~~~~~~~~~
./List.hpp:53:13: note: in instantiation of member function 'ft::list<int, std::allocator<int> >::begin' requested
here
assign(x.begin(), x.end());
./../Iterator/BidirectionalIterator.hpp:45:3: note: candidate constructor not viable: no known conversion from
'Elem<ft::list<int, std::allocator<int> >::value_type> *const' (aka 'Elem<int> *const') to
'Elem<ft::BidirectionalIterator<const int>::value_type> *' (aka 'Elem<const int> *') for 1st argument
BidirectionalIterator(Elem<value_type> *ptr) {
I don't know how to fix that problem. I already tried to delete the const attribute from my copy constructor and it works, but it needs to be const (for the rest of my project cause I'm implementing the relational operators that call a const list, and also to respect the original container constructor).
Does anyone have an idea?
You try to create an Elem<const int>* from an Elem<int> *const.
I suggest making the iterator's pointer Elem<std::remove_const_t<T>>* (even for a const_iterator) but let dereferencing a const_iterator return a T const& or T const *.
Example:
template <class T>
class BidirectionalIterator {
public:
using value_type = T;
using reference = value_type&;
using pointer = value_type*;
using size_type = std::size_t;
BidirectionalIterator() : _ptr(nullptr) {};
BidirectionalIterator(Elem<std::remove_const_t<value_type>>* ptr) : _ptr(ptr) {};
BidirectionalIterator(const BidirectionalIterator& x) {
_ptr = x._ptr;
};
BidirectionalIterator& operator=(const BidirectionalIterator& x) {
_ptr = x._ptr;
return *this;
};
reference operator*() const { return _ptr->data; }
pointer operator->() const { return &_ptr->data; }
Elem<std::remove_const_t<value_type>>* _ptr;
};
A slightly better version to let you create lists of const Ts and to also let you convert iterators to const_iterators (but not the other way around) to be able to compare iterators could look like this:
#include <memory>
#include <type_traits>
template <class T, class ElemType> // const or non-const T and the type used in Elem
class BidirectionalIterator {
public:
using value_type = T;
using reference = value_type&;
using pointer = value_type*;
using size_type = std::size_t;
BidirectionalIterator() : _ptr(nullptr) {};
BidirectionalIterator(Elem<ElemType>* ptr) : _ptr(ptr) {};
// let a conversion constructor of the const_iterator read _ptr
friend class BidirectionalIterator<const ElemType, ElemType>;
// enable a const_iterator to be created from a non-const iterator via
// a conversion constructor
template<typename U = T, typename V = ElemType,
std::enable_if_t<std::is_const_v<U>&&!std::is_const_v<V>, int> = 0
>
BidirectionalIterator(const BidirectionalIterator<ElemType, ElemType>& x) :
_ptr(x._ptr) {}
// normal copy ctor
BidirectionalIterator(const BidirectionalIterator& x) : _ptr(x._ptr) {}
BidirectionalIterator& operator=(const BidirectionalIterator& x) {
_ptr = x._ptr;
return *this;
};
// the conversion constructor lets you compare a const_iterator and an iterator
bool operator==(const BidirectionalIterator& rhs) const {
return _ptr == rhs._ptr;
}
bool operator!=(const BidirectionalIterator& rhs) const {
return !(_ptr == rhs._ptr);
}
reference operator*() const { return _ptr->data; }
pointer operator->() const { return &_ptr->data; }
private:
Elem<ElemType>* _ptr;
};
// iterator == const_iterator, swap order to use member operator==
template<typename T>
bool operator==(const BidirectionalIterator<T, T>& a,
const BidirectionalIterator<const T, T>& b) {
return b == a;
}
// iterator != const_iterator, swap order to use member operator!=
template<typename T>
bool operator!=(const BidirectionalIterator<T, T>& a,
const BidirectionalIterator<const T, T>& b) {
return b != a;
}
With this iterator definition, you'd need to define your iterator and const_iterator slightly different.
template <class T, class Alloc = std::allocator<T>>
class list {
public:
using value_type = T;
using iterator = BidirectionalIterator<T, T>;
using const_iterator = BidirectionalIterator<const T, T>;
//...
I have implemented a doubly linked list with iterators(regular and const) and it seems working ok, but I can't understand several things:
1) As I know one of the ways to implement iterators is inherit from std::iterator< ... > mentioning in template parameters all the necessary types, then typedef them in your class ... but why we need difference_type of two iterators ? bidirectional iterator must not overload operator - () inside so is it ok to not remove difference type ?
2) I know that it's not the best approach to duplicate logic of similar things (in my case regular iterator and const_iterator are two different classes) but that's what I did at the moment due to my basic knowledge about c++ templates. I made const_iterator as a friend for iterator and made implicit conversions from T (node value type) to const T using special constructor const_iterator(iterator &other) so when returning references and pointers from operator *() it and operator->() it automatically converts internal ptr->_node_value (list node data) to const version. Is this approach ok enough ??
template <typename T>
struct DLList_node
{
T _node_value;
DLList_node<T> *_next, *_prev;
DLList_node<T>(const T& = T(), DLList_node<T> * = NULL, DLList_node<T> * = NULL);
};
template <typename T>
class DLList;
//non-const regular iterator
template<typename T>
class DLList_iterator;
//const iterator
template<typename T>
class DLList_const_iterator;
template<typename T>
class DLList_iterator :
public std::iterator< std::bidirectional_iterator_tag, T, T*, T& >
{
public:
friend class DLList<T>;
//const iterator must have access to node pointer
friend class DLList_const_iterator<T>;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
typedef std::bidirectional_iterator_tag iterator_category;
typedef DLList_node<T>* node_ptr;
DLList_iterator()
: ptr(NULL)
{}
DLList_iterator(node_ptr _ptr): ptr(_ptr)
{}
reference operator*() const
{
assert(ptr != NULL && "list iterator not dereferencable!");
return ptr->_node_value;
}
pointer operator->() const
{
assert(ptr != NULL && "list iterator not dereferencable!");
return &(ptr->_node_value);
}
bool operator == (const DLList_iterator& other) const
{
return ptr == other.ptr;
}
bool operator != (const DLList_iterator& other) const
{
return ptr != other.ptr;
}
//pre incerement
DLList_iterator& operator++()
{
assert(ptr != NULL && "list iterator not incrementable!");
ptr = ptr->_next;
return *this;
}
//post increment
DLList_iterator operator++(int)
{
assert(ptr != NULL && "list iterator not incrementable!");
DLList_iterator tmp(*this);
ptr = ptr->_next;
return tmp;
}
DLList_iterator& operator--()
{
assert(ptr != NULL && "list iterator not decrementable!");
ptr = ptr->_prev;
return *this;
}
DLList_iterator operator--(int)
{
assert(ptr != NULL && "list iterator not decrementable!");
DLList_iterator tmp(*this);
ptr = ptr->_prev;
return tmp;
}
private:
node_ptr ptr;
};
//const iterator
template<typename T>
class DLList_const_iterator :
public std::iterator< std::bidirectional_iterator_tag, const T, const T*, const T& >
{
public:
friend class DLList<T>;
typedef const T value_type;
typedef ptrdiff_t difference_type;
typedef const T* const_pointer;
typedef const T& const_reference;
typedef std::bidirectional_iterator_tag iterator_category;
typedef DLList_node<T>* node_ptr;
DLList_const_iterator()
: cptr(NULL)
{}
DLList_const_iterator(DLList_iterator<T>& _iter): cptr(_iter.ptr)
{}
DLList_const_iterator(node_ptr _ptr): cptr(_ptr)
{}
const_reference operator*() const
{
assert(cptr != NULL && "list iterator not dereferencable!");
return cptr->_node_value;
}
const_pointer operator->() const
{
assert(cptr != NULL && "list iterator not dereferencable!");
return &(cptr->_node_value);
}
bool operator == (const DLList_const_iterator & other) const
{
return cptr == other.cptr;
}
bool operator != (const DLList_const_iterator & other) const
{
return cptr != other.cptr;
}
//pre incerement
DLList_const_iterator & operator++()
{
assert(cptr != NULL && "list iterator not incrementable!");
cptr = cptr->_next;
return *this;
}
//post increment
DLList_const_iterator operator++(int)
{
assert(cptr != NULL && "list iterator not incrementable!");
DLList_const_iterator tmp(*this);
cptr = cptr->_next;
return tmp;
}
DLList_const_iterator & operator--()
{
assert(cptr != NULL && "list iterator not decrementable!");
cptr = cptr->_prev;
return *this;
}
DLList_const_iterator operator--(int)
{
assert(cptr != NULL && "list iterator not decrementable!");
DLList_iterator tmp(*this);
cptr = cptr->_prev;
return tmp;
}
private:
node_ptr cptr;
};
template <typename T>
class DLList
{
private:
node_ptr _begin, _tail;
public:
typedef DLList_iterator<T> iterator;
typedef DLList_const_iterator<T> const_iterator;
typedef DLList_node<T>* node_ptr;
typedef DLList_node<T> node;
typedef typename DLList_iterator<T>::value_type value_type;
typedef typename DLList_iterator<T>::difference_type difference_type;
typedef typename DLList_iterator<T>::pointer pointer;
typedef typename DLList_iterator<T>::reference reference;
typedef typename DLList_const_iterator<T>::const_pointer const_pointer;
typedef typename DLList_const_iterator<T>::const_reference const_reference;
//member functions and constructors here
//...
};
1) As I know one of the ways to implement iterators is inherit from std::iterator< ... > mentioning in template parameters all the necessary types,
Yes.
then typedef them in your class ...
No. That's why you inherit from std::iterator so you don't have to bother doing that.
but why we need difference_type of two iterators ?
Note that std::iterator defaults most of the standard types. You only need to specify a difference type if you are doing something funky and none standard.
bidirectional iterator must not overload operator - () inside so is it ok to not remove difference type ?
You don't need to mention any of the types. std::iterator does all that work for you.
2) I know that it's not the best approach to duplicate logic of similar things (in my case regular iterator and const_iterator are two different classes) but that's what I did at the moment due to my basic knowledge about c++ templates.
Looking at your class's they are basically the same apart from some differences in retrieval. Why not move all the same code into a base class.
template<typename T>
class DLList_Base_iterator :
public std::iterator< std::bidirectional_iterator_tag, T, T*, T& >
{
// Common code
protected: // allow all concrete iterators to see pointer.
node_ptr ptr;
};
template<typename T>
class DLList_iterator : public DLList_Base_iterator<T>
{
DLList_iterator(): public DLList_Base_iterator<T>(NULL) {}
DLList_iterator(node_ptr _ptr): public DLList_Base_iterator<T>(_ptr) {}
reference operator*() const {return cptr->_node_value;}
pointer operator->() const {return &(cptr->_node_value);}
};
template<typename T>
class DLList_const_iterator : public DLList_Base_iterator<T>
{
DLList_const_iterator(): public DLList_Base_iterator<T>(NULL) {}
DLList_const_iterator(node_ptr _ptr): public DLList_Base_iterator<T>(_ptr) {}
const_reference operator*() const {return cptr->_node_value;}
const_pointer operator->() const {return &(cptr->_node_value);}
};
I made const_iterator as a friend for iterator and made implicit conversions from T (node value type) to const T using special constructor const_iterator(iterator &other) so when returning references and pointers from operator *() it and operator->() it automatically converts internal ptr->_node_value (list node data) to const version. Is this approach ok enough ??
I would just add a conversion operator to the non-const iterator that allows you to convert it to a const version.
template<typename T>
class DLList_iterator : public DLList_Base_iterator<T>
{
operator DLList_const_iterator<T> {
return DLList_const_iterator<T>(cptr);
}
};
Following on from this question: std::list implementation & pointer arithemetic.
I want to implement a list iterator that is interchangeable with other common containers types and their respective iterators, so I want to use operators such as: --, ++, * and be able to declare iterators as normal, so: list::iterator iter = list.begin();
The --, ++ operators now work as they should, but I ran up against the problem of de-referencing the iterator, as structs can't return a value: T iterator::operator*()
template <class T>
struct element {
element<T> *prev = NULL;
element<T> *next = NULL;
T data;
};
template <typename T>
class list {
public:
list::list();
element<T>* current;
struct iterator{
element<T>* iterator::operator++(){
this = *this->next; ..whatever it works
}
element<T>* iterator::operator--()
T iterator::operator*()
}
};
the iterators could look something like this:
(declared inside the list template)
struct iterator;
struct const_iterator : public std::iterator<std::bidirectional_iterator_tag, const T>
{
const_iterator() = default;
T operator*() { return itm->data; }
const T* operator->() { return &(itm->data); }
const_iterator operator++() { itm = itm->next; return *this; }
const_iterator operator--() { itm = itm->prev; return *this; }
const_iterator operator++(int) { const_iterator ret=*this; itm = itm->next; return ret; }
const_iterator operator--(int) { const_iterator ret=*this; itm = itm->prev; return ret; }
bool operator==(const_iterator oth) const { return itm==oth.itm; }
bool operator!=(const_iterator oth) const { return itm!=oth.itm; }
private:
element<T>* itm = nullptr;
const_iterator(element<T>* i) : itm(i) {}
friend
class list;
friend
struct iterator;
};
struct iterator : public std::iterator<std::bidirectional_iterator_tag, T>
{
iterator() = default;
T& operator*() { return itm->data; }
T* operator->() { return &(itm->data); }
iterator operator++() { itm = itm->next; return *this; }
iterator operator--() { itm = itm->prev; return *this; }
iterator operator++(int) { iterator ret=*this; itm = itm->next; return ret; }
iterator operator--(int) { iterator ret=*this; itm = itm->prev; return ret; }
bool operator==(iterator oth) const { return itm==oth.itm; }
bool operator!=(iterator oth) const { return itm!=oth.itm; }
operator const_iterator() { return {itm}; }
private:
element<T>* itm = nullptr;
iterator(element<T>* i) : itm(i) {}
friend
class list;
};
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
this needs
#include <iterator>
to compile
I have a custom container class for which I'd like to write the iterator and const_iterator classes.
I never did this before and I failed to find an appropriate how-to. What are the guidelines regarding iterator creation, and what should I be aware of ?
I'd also like to avoid code duplication (I feel that const_iterator and iterator share many things; should one subclass the other ?).
Foot note: I'm pretty sure Boost has something to ease this but I can't use it here, for many stupid reasons.
Choose type of iterator which fits your container: input, output, forward etc.
Use base iterator classes from standard library. For example, std::iterator with random_access_iterator_tag.These base classes define all type definitions required by STL and do other work.
To avoid code duplication iterator class should be a template class and be parametrized by "value type", "pointer type", "reference type" or all of them (depends on implementation). For example:
// 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;
Notice iterator_type and const_iterator_type type definitions: they are types for your non-const and const iterators.
See Also: standard library reference
EDIT: std::iterator is deprecated since C++17. See a relating discussion here.
I'm going to show you how you can easily define iterators for your custom containers, but just in case I have created a c++11 library that allows you to easily create custom iterators with custom behavior for any type of container, contiguous or non-contiguous.
You can find it on Github
Here are the simple steps to creating and using custom iterators:
Create your "custom iterator" class.
Define typedefs in your "custom container" class.
e.g. typedef blRawIterator< Type > iterator;
e.g. typedef blRawIterator< const Type > const_iterator;
Define "begin" and "end" functions
e.g. iterator begin(){return iterator(&m_data[0]);};
e.g. const_iterator cbegin()const{return const_iterator(&m_data[0]);};
We're Done!!!
Finally, onto defining our custom iterator classes:
NOTE: When defining custom iterators, we derive from the standard iterator categories to let STL algorithms know the type of iterator we've made.
In this example, I define a random access iterator and a reverse random access iterator:
//-------------------------------------------------------------------
// 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;}
};
//-------------------------------------------------------------------
Now somewhere in your custom container class:
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;
};
They often forget that iterator must convert to const_iterator but not the other way around. Here is a way to do that:
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;
};
In the above notice how IntrusiveSlistIterator<T> converts to IntrusiveSlistIterator<T const>. If T is already const this conversion never gets used.
Boost has something to help: the Boost.Iterator library.
More precisely this page: boost::iterator_adaptor.
What's very interesting is the Tutorial Example which shows a complete implementation, from scratch, for a custom type.
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(); }
};
The main point, as has been cited already, is to use a single template implementation and typedef it.
I don't know if Boost has anything that would help.
My preferred pattern is simple: take a template argument which is equal to value_type, either const qualified or not. If necessary, also a node type. Then, well, everything kind of falls into place.
Just remember to parameterize (template-ize) everything that needs to be, including the copy constructor and operator==. For the most part, the semantics of const will create correct behavior.
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;
There are plenty of good answers but I created a template header I use that is quite concise and easy to use.
To add an iterator to your class it is only necessary to write a small class to represent the state of the iterator with 7 small functions, of which 2 are optional:
#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 equals(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);
};
Then you can use it as you would expect from an STL iterator:
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
}
}
I hope it helps.
I came across this post and was surprised that a simple method is not really mentioned here. Using a pointer to the value like how std::iterator describes is obviously a very generic approach. But you might be able to get away with something much simpler. Of course this is a simplistic approach and might not always be sufficient, but in case it is, I am posting it for the next reader.
Most probably the underlying type in your class is an STL container which already has defined the iterators for you. If that is the case, you can simply use their defined iterators and don't really need to make your own.
Here is an example:
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
};
i'm interested to know how correct this is, but seems to work as a roll-your-own iterator to internal data storage
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>>;
Then i just add these to my class, and seems to work as expected.
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;
};
the only thing is that to make it with the std::cbegin(), std::rcbegin(), std::cend() and std::rcend() functions I have to extend the std namespace:
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(); }
}
Check this below code, it works
#define MAX_BYTE_RANGE 255
template <typename T>
class string
{
public:
typedef char *pointer;
typedef const char *const_pointer;
typedef __gnu_cxx::__normal_iterator<pointer, string> iterator;
typedef __gnu_cxx::__normal_iterator<const_pointer, string> const_iterator;
string() : length(0)
{
}
size_t size() const
{
return length;
}
void operator=(const_pointer value)
{
if (value == nullptr)
throw std::invalid_argument("value cannot be null");
auto count = strlen(value);
if (count > 0)
_M_copy(value, count);
}
void operator=(const string &value)
{
if (value.length != 0)
_M_copy(value.buf, value.length);
}
iterator begin()
{
return iterator(buf);
}
iterator end()
{
return iterator(buf + length);
}
const_iterator begin() const
{
return const_iterator(buf);
}
const_iterator end() const
{
return const_iterator(buf + length);
}
const_pointer c_str() const
{
return buf;
}
~string()
{
}
private:
unsigned char length;
T buf[MAX_BYTE_RANGE];
void _M_copy(const_pointer value, size_t count)
{
memcpy(buf, value, count);
length = count;
}
};