I have some templates which can either have a map or a vector as underlying container. I would like the template to be able to expose const iterators to the elements. Most information I have read on how to expose iterators (such as this accu article) uses a form of
typedef std::vector<int>::iterator iterator;
typedef std::vector<int>::const_iterator const_iterator;
iterator begin() { return values.begin(); }
iterator end() { return values.end(); }
That doesn't really work for the template that uses the map though, because to access elements, the template can't use it->SomeMemberFunc() anymore, but rather needs to use it->second.SomeMemberFunc(). So I am looking to expose an iterator to the map elements which doesn't give access to the keys but only the values of the map.
How would I accomplish this?
A workaround1
#include <map>
#include <iterator>
template <typename Iter>
struct map_iterator : public std::iterator<std::bidirectional_iterator_tag,
typename Iter::value_type::second_type>
{
map_iterator() {}
map_iterator(Iter j) : i(j) {}
map_iterator& operator++() { ++i; return *this; }
map_iterator& operator--() { --i; return *this; }
bool operator==(map_iterator j) const { return i == j.i; }
bool operator!=(map_iterator j) const { return !(*this == j); }
typename map_iterator::reference operator*() { return i->second; }
typename map_iterator::pointer operator->() { return &i->second; }
map_iterator operator--(int) { return std::prev(--(*this)); }
map_iterator operator++(int) { return std::next((++*this)); }
protected:
Iter i;
};
template <typename Iter>
inline map_iterator<Iter> make_map_iterator(Iter j) {
return map_iterator<Iter>(j);
}
And then
int main() {
std::map<int,std::string> m {{1, "Hi"},{2, "Bye"}};
for (auto i=make_map_iterator(m.begin()); i!=make_map_iterator(m.end());i++)
cout << *i << endl;
}
Live code.
I want to implement a cyclic list based on std::list. I want to profit from the benfits of the list but add one specific feature: its iterator operators ++ and -- should hop over the edges and operations (insert/erase) must not invalidate existing iterators. My skills in handling templates are weak and to understand the std containers is an impossible act for me. Hence i need your help. By now im not that far :D. Sorry but even the numerous posts dont help me any further.
EDIT:
Well after a lot of work, a steeeep learnig curve, the failed approach to inherit from std::list::iterator, a short-term depression and a abasing return to your approaches (yes, you all were right) I finally made it. Inspired by all your contibutions, I can now post what I did the last ... about 12 hours :D Basicly what you suggested, but with nice little operators.
#pragma once
#include <list>
using std::list;
template<class T>
class cyclic_iterator;
template<class T>
class cyclicList : public list<T>
{
public:
typedef cyclic_iterator<T> cyclic_iterator;
cyclic_iterator cycbegin()
{// not the purpose, but needed for instanziation
return cyclic_iterator( *this, this->begin());
}
cyclic_iterator cycend()
{// not the purpose, but needed for instanziation
return cyclic_iterator( *this, this->end());
}
};
template<class T>
class cyclic_iterator
{
public:
// To hop over edges need to know the container
cyclic_iterator(){}
cyclic_iterator(typename list<T>::iterator i)
: mIter(i){}
cyclic_iterator(list<T> &c)
: mContainer(&c){}
cyclic_iterator(list<T> &c, typename list<T>::iterator i)
: mContainer(&c), mIter(i){}
cyclic_iterator<T>& operator=(typename list<T>::iterator i)
{// assign an interator
mIter = i;
return *this;
}
cyclic_iterator<T>& operator=(list<T> &c)
{// assign a container
mContainer = &c;
return *this;
}
bool operator==(const cyclic_iterator<T>& rVal) const
{// check for equality
return (this->mIter == rVal.mIter && this->mContainer == rVal.mContainer) ? true : false;
}
bool operator!=(const cyclic_iterator<T>& rVal) const
{// check for inequality
return !(this->operator==(rVal));
}
cyclic_iterator<T>& operator++()
{// preincrement
++mIter;
if (mIter == mContainer->end())
{ mIter = mContainer->begin(); }
return *this;
}
cyclic_iterator<T> operator++(int)
{ // postincrement
cyclic_iterator<T> tmp = *this;
++*this;
return tmp;
}
cyclic_iterator<T>& operator--()
{// predecrement
if (mIter == mContainer->begin())
mIter = --mContainer->end();
else --mIter;
return *this;
}
cyclic_iterator<T> operator--(int)
{// postdecrement
cyclic_iterator<T> tmp = *this;
--*this;
return tmp;
}
cyclic_iterator<T>& operator+=(int j)
{// hop j nodes forward
for (int i = 0; i < j; ++i)
++(*this);
return *this;
}
cyclic_iterator<T>& operator-=(int j)
{// hop j nodes backwards
for (int i = 0; i < j; ++i)
--(*this);
return *this;
}
T& operator*()
{
return *mIter;
}
typename list<T>::iterator & getStdIterator()
{
return mIter;
}
private:
list<T>* mContainer;
typename list<T>::iterator mIter;
};
Can't you just make a different iterator type?
#include <iterator>
#include <list>
template <typename T, typename Alloc>
struct cyclic_iterator
: std::iterator<typename std::list<T, Alloc>::iterator::iterator_category, T>
{
typedef std::list<T, Alloc> list_type;
cyclic_iterator & operator++()
{
++iter;
if (iter == container.end()) { iter = container.begin(); }
return *this;
}
T & operator*() { return *iter; }
cyclic_iterator(typename list_type::iterator it, list_type & l)
: iter(it)
, container(l)
{
if (it == container.end()) { it = container.begin(); }
}
// everything else
private:
typename list_type::iterator iter;
list_type & container;
};
With a helper:
template <typename List>
cyclic_iterator<typename List::value_type, typename List::allocator_type>
make_cyclic_iterator(typename List::iterator it, List & l)
{
return cyclic_iterator<typename List::value_type, typename List::allocator_type>(it, l);
}
Usage:
// goes round and round forever
for (auto ci = make_cyclic_iterator(mylist.begin(), mylist); ; ++ci)
{
std::cout << *ci << std::endl;
}
(With a few modifications, this code could be made to work on any container that exposes begin/end iterators.)
It's not possible. The iterators and the implementation of the end element is implementation specific and not customizable. Containers were not designed for that kind of thing and it would make them really hard. You will have to go through the pain of implementing this yourself. Keep in mind that this gets very tricky because a cyclic list doesn't have a real past-the-end iterator and iterators aren't really able to handle that kind of situation. Some libraries have a Circulator concept to deal with circular structures.
NB: Inheriting from a standard container is a bad idea.
Of course you can implement it using std::list, but first you should encapsulate list in your class and do not derive from it second you must implement your own iterator to accomplish this, but since circular lists are fixed in size I prefer a container with a fixed size and linear memory like std::array or std::vector.
template<
class T,
class Container = std::vector<T>
>
class circular_list {
public:
// Following typedef are required to make your class a container
typedef typename Container::size_type size_type;
typedef typename Container::difference_type difference_type;
typedef typename Container::pointer pointer;
typedef typename Container::const_pointer const_pointer;
typedef typename Container::reference reference;
typedef typename Container::const_reference const_reference;
typedef typename Container::value_type value_type;
public:
class iterator : std::iterator<std::bidirectional_iterator_tag, value_type> {
public:
iterator() : c_(nullptr) {}
iterator(circular_buffer& c, size_type index)
: c_( &c.c_ ), index_( index ) {}
reference operator* () const {
return (*c_)[index_];
}
iterator& operator++ () {
if( ++index_ >= c_->size() ) index_ = 0;
return *this;
}
iterator& operator-- () {
if( index_ == 0 ) index_ = c_->size() - 1; else --index_;
return *this;
}
private:
size_type index_;
Container* c_;
};
public:
void push( const_reference val ) {add item to the container}
reference current() {return current item from the container}
void pop() {remove item from the container}
size_type size() const {return c_.size();}
iterator begin() {return iterator( *this, 0 );}
iterator end() {return iterator( *this, size() );}
private:
friend iterator;
Container c_;
}
I am implementing an array in C++ (for various reasons, one of them is to get to know custom iterators).
While testing it out I noticed that it does not compile in g++ 4.4 but works fine in Visual Studio 2010.
I have included an example program below. In that, I print out the last but one value of a toy array that I implemented using templates with the accompanying iterator class. The compiler error I get is the following:
$ g++-4.4 -ansi -Wall -std=c++0x mybuffer.cpp -o mybuffer
In file included from /usr/include/c++/4.4/bits/stl_algobase.h:69,
from /usr/include/c++/4.4/memory:49,
from mybuffer.cpp:1:
/usr/include/c++/4.4/bits/stl_iterator.h: In member function ‘std::reverse_iterator<_Iterator> std::reverse_iterator<_Iterator>::operator+(typename std::iterator_traits<_Iter>::difference_type) const [with _Iterator = CMyItr<CMyBuff<double>, double>]’:
mybuffer.cpp:205: instantiated from here
/usr/include/c++/4.4/bits/stl_iterator.h:221: error: passing ‘const CMyItr<CMyBuff<double>, double>’ as ‘this’ argument of ‘CMyItr<T, typename T::value_type> CMyItr<T, elem_type>::operator-(typename T::difference_type) [with T = CMyBuff<double>, elem_type = double]’ discards qualifiers
The error occurs if I do *(RItr+1) operation, not if I do *(++RITr) operation. It works in Visual Studio 2010 in either case however.
I get the same compiler error in g++ 2.95 also. Haven't tried in Visual Studio 6.
Can somebody explain what I am doing wrong and how to fix it?
Thanks.
PS: Using g++ 4.6 is a whole another problem, but that is for later.
//-------------------------------------------- example code ----------------//
//------------------------------------------------------------------------------//
//------------------------------------------------------------------------------//
#include <memory>
#include <iostream>
#include <iterator>
template < typename T, typename elem_type=typename T::value_type>
class CMyItr {
public:
typedef T BuffType;
typedef CMyItr<T> self_type;
typedef CMyItr<self_type, elem_type> iterator;
typedef typename std::bidirectional_iterator_tag iterator_category;
typedef typename BuffType::value_type value_type;
typedef typename BuffType::size_type size_type;
typedef typename BuffType::pointer pointer;
typedef typename BuffType::const_pointer const_pointer;
typedef typename BuffType::reference reference;
typedef typename BuffType::const_reference const_reference;
typedef typename BuffType::difference_type difference_type;
CMyItr( BuffType *pB, size_type pos):
PtrItr_(pB), PtrPos_(pos){
};
friend class CMyItr< const T, const elem_type>;
elem_type &operator*(){
return (*PtrItr_)[PtrPos_];
};
elem_type *operator->(){
return &(operator*());
};
self_type & operator++(){
++PtrPos_;
return *this;
};
self_type operator++(int){
self_type tmp(*this);
++(*this);
return tmp;
};
self_type operator+(difference_type n) {
self_type tmp(*this);
tmp.PtrPos_ = tmp.PtrPos_ + n;
return tmp;
};
self_type &operator+=(difference_type n){
PtrPos_ = PtrPos_ + n;
return *this;
};
self_type & operator--(){
--PtrPos_;
return *this;
};
/*!
The decrement operator which decrements the position index.
*/
self_type operator--(int){
self_type tmp(*this);
--(*this);
return tmp;
};
self_type operator-(difference_type n) {
self_type tmp(*this);
tmp.PtrPos_ = tmp.PtrPos_ - n;
return tmp;
};
self_type &operator-=(difference_type n){
PtrPos_ -= n;
return *this;
};
bool operator!=(const self_type &other) const {
return PtrPos_ != other.PtrPos_ && PtrItr_ == other.PtrItr_;
};
bool operator==(const self_type &other) const {
return PtrPos_ == other.PtrPos_ && PtrItr_ == other.PtrItr_;
};
private:
BuffType * PtrItr_;
size_type PtrPos_;
};
//----------------------------------------------------------------------//
//----------------------------------------------------------------------//
template < typename T > class CMyBuff {
public:
enum {default_size = 4 };
typedef CMyBuff<T> self_type;
typedef T value_type;
typedef T & reference;
typedef const T & const_reference;
typedef T * pointer;
typedef const T * const_pointer;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef CMyItr<self_type> iterator;
typedef CMyItr<const self_type> const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const iterator> const_reverse_iterator;
/*! Starting for forward iterator.*/
iterator begin(){
return iterator(this, 0);
};
/*! Forward iterator should go till here.*/
iterator end(){
return iterator(this, Size_);
};
/*! Starting for constant forward iterator.*/
const_iterator begin() const {
return const_iterator(this, 0);
};
/*! Constant forward iterator should go till here.*/
const_iterator end() const {
return const_iterator(this, Size_);
};
/*! Reverse iterator starts from here.*/
reverse_iterator rbegin(){
return reverse_iterator(end());
}
/*! Reverse iterator end.*/
reverse_iterator rend() {
return reverse_iterator(begin());
}
/*! Constant reverse iterator starting point.*/
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
/*! Constant reverse iterator should end here.*/
const_reverse_iterator rend() const {
return const_reverse_iterator( begin());
}
/* Ctor for my buffer*/
explicit CMyBuff(size_type capacity = default_size):
Ptr_(NULL),
Size_(capacity) {
Ptr_ = new value_type [sizeof(value_type) * Size_];
Ptr_[0] = 0;
Ptr_[1] = 1;
Ptr_[2] = 8;
Ptr_[3] = 27;
};
~CMyBuff() {
delete [] Ptr_;
}
reference operator[](size_type i){
return rAtUnChecked(i);
};
const_reference operator[](size_type i) const {
return rAtUnChecked(i);
};
size_type size() const {
return Size_;
};
reference rAtUnChecked(size_type k) const {
return Ptr_[k];
};
private:
pointer Ptr_;
size_type Size_;
};
//------------------------------------------------------------------//
//----------------------------------------- MAIN ------------------//
// Use the following command line to compile:
// g++-4.4 -ansi -Wall -std=c++0x mybuffer.cpp -o mybuffer
int main(){
CMyBuff<double> Buffer;
CMyBuff < double >::reverse_iterator RItr = Buffer.rbegin();
//prints last but one element
std::cout << *(++RItr) << std::endl;
// The following doesn't compile on g++. Get const related error
// containing "discards qualifier"
//std::cout << *(RItr + 1) << std::endl;
return 0;
}
//-------------------------------------------------------------------------------//
//-------------------------------------------- code END ----------------//
Your iterator's operator- doesn't mutate state so it should be const.
This may not be an answer, but can you try following:
change, difference_type n to const difference_type &n
Declare operator +() and operator -() as const (as they don't affect this)
See if it helps.
The code is as follows
template<class T>
class arrayList {
public:
// constructor, copy constructor and destructor
arrayList(int initialCapacity = 10);
arrayList(const arrayList<T>&);
~arrayList() {
delete[] element;
}
class seamlessPointer;
seamlessPointer begin() {
return seamlessPointer(element);
}
seamlessPointer end() {
return seamlessPointer(element + listSize);
}
// iterator for arrayList
class iterator
{
public:
// typedefs required by C++ for a bidirectional iterator
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
// constructor
iterator(T* thePosition = 0) {position = thePosition;}
// dereferencing operators
T& operator*() const {return *position;}
T* operator->() const {return position;}
// increment
iterator& operator++();
{++position; return *this;}
iterator operator++(int);
// decrement
iterator& operator--();
iterator operator--(int) ;
// equality testing
bool operator!=(const iterator right) ;
bool operator==(const iterator right) ;
protected:
T* position;
}; // end of iterator class
class seamlessPointer: public arrayList<T>::iterator {
public:
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
// constructor
seamlessPointer(T *thePosition);
seamlessPointer(const seamlessPointer & rhs);
//arithmetic operators
seamlessPointer operator+(int n) ;
seamlessPointer operator-(int n) ;
};
protected:
T* element; // 1D array to hold list elements
int arrayLength; // capacity of the 1D array
int listSize; // number of elements in list
};
c:\wascana\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/stl_algo.h:5250:4: error: no match for 'operator-' in '__last - __first'
Looking over your code it appears that you've got the right kinds of ideas by including those typedefs with your own iterator implementation. However, why go to all the trouble in the first place when something else could do it for you? Have you looked at iterator_traits or the standard iterator? They just add typedefs to your code which will aid you in developing new iterator types.
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;
}
};