303

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.

svick
  • 236,525
  • 50
  • 385
  • 514
ereOn
  • 53,676
  • 39
  • 161
  • 238
  • 3
    From the set of related questions: [Can 'iterator' type just subclass 'const_iterator'?](http://stackoverflow.com/questions/2844466/can-iterator-type-just-subclass-const-iterator) [How do you avoid code duplication when implementing const and non-const iterators?](http://stackoverflow.com/questions/2150192/how-do-you-avoid-code-duplication-when-implementing-const-and-non-const-iterators) – sbi Aug 27 '10 at 08:54
  • Is the GoF Iterator Pattern being considered at all ? – DumbCoder Aug 27 '10 at 09:02
  • 4
    @DumbCoder: In C++ it is often desirable to have iterators that are STL-compliant, because they will work nicely with all the existing containers and algorithms provided by the STL. Though the concept is similar, there are some differences to the pattern proposed by the GoF. – Björn Pollex Aug 27 '10 at 09:51
  • I've posted sample of custom iterator [here](http://stackoverflow.com/a/39767072/1044966) – Valdemar_Rudolfovich Sep 29 '16 at 09:55
  • 2
    The complexity of these answers suggests that C++ is either a language unworthy of anything other than homework assignments for up-jumped undergrads, or the answers are overcomplicated and wrong. There must be an easier way in Cpp? Like CMake and Automake before it relative to make, raw C boiled out of a python prototype seems much easier than this. – Chris Jan 11 '20 at 22:57
  • I recently posted an article about this topic: [C++ Senioreas - Iterators Customization](https://cppsenioreas.wordpress.com/2020/10/04/maintain-your-iterations-iterators-customization-part-3/) – Coral Kashri Oct 04 '20 at 22:49
  • 2
    @Chris C++ is certainly worthy. The complexity of C++ and the learning curve is sort of a payment for its relatively unique attributes of extremely optimized abstractions. Some say zero-cost abstractions and in many cases this is exactly what happens in modern C++. – Pavel Šimerda Mar 09 '21 at 21:38
  • 2
    @PavelŠimerda haha Yeah I totally disagree with my comment at this point in general. I think I was thinking more along the lines of "there must be an easier way" – Chris Mar 09 '21 at 21:39

9 Answers9

175
  • 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.

Andrey
  • 3,667
  • 6
  • 27
  • 36
  • @Andrey Could you explain the distinction between value type, pointer type, and reference type? –  Jul 26 '12 at 19:52
  • 8
    @Potatoswatter: Have not downvoted this, but, hey, `random_access_iterator` is not in the standard and the answer does not handle the mutable to const conversion. You probably want to inherit from, e.g. `std::iterator` though. – Yakov Galka Oct 08 '12 at 14:55
  • 2
    Yeah, I'm not quite sure how this works. If I have the method `RefType operator*() { ... }`, I'm one step closer -- but it doesn't help, because I still need `RefType operator*() const { ... }`. – Translunar Sep 05 '13 at 05:42
  • 31
    `std::iterator` [has been deprecated](http://en.cppreference.com/w/cpp/iterator/iterator) – diapir Feb 14 '18 at 08:25
  • 25
    If this is deprecated, what is the proper "new" way of doing it instead? – SasQ Sep 24 '19 at 07:59
  • 2
    @SasQ You just define the member types yourself (which is not that much extra work) - or create a similar template as `std::iterator` if you liked it. – Ted Lyngmo Dec 04 '19 at 18:35
  • 1
    @SasQ, I am shamelessly self-promoting myself, but take a look at my answer, for me, it is by far the simplest way of implementing iterators in C++: https://stackoverflow.com/a/44380342/2905274 – VinGarcia Sep 25 '20 at 13:59
  • 1
    I am confused by this answer. You state to inherit from `std::iterator` but then don't appear to do this. Answer is also not particularly helpful since the class `MyIterator` is blank and there isn't much information about what it should contain. I'm not sure how to interpret this. – FreelanceConsultant Dec 10 '21 at 17:01
82

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:

  1. Create your "custom iterator" class.
  2. Define typedefs in your "custom container" class.
    • e.g. typedef blRawIterator< Type > iterator;
    • e.g. typedef blRawIterator< const Type > const_iterator;
  3. 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]);};
  4. 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:

  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 = 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;
    };
    //-------------------------------------------------------------------
    
  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 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;
};
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Enzo
  • 1,042
  • 7
  • 7
  • I think the operator+ and operator- may have the operations backwards. It looks like operator+ is subtracting movement from the pointer not adding and operator- is adding it. This seems backwards – Beached Jun 18 '17 at 01:13
  • 1
    It's for the reverse iterator, operator+ should go backwards and operator- should go forward – Enzo Sep 06 '17 at 18:39
  • 3
    Awesome. The accepted answer is too high level. This is awesome. Thank you Enzo. – FernandoZ Nov 19 '17 at 21:11
  • You need to edit your answer. Assuming m_data was allocated with m_size elements you get Undefined Behavior: `m_data[m_size]` is UB. You can simply fix it by replacing it with `m_data+m_size`. For reverse iterators, both `m_data[-1]` and `m_data-1` are incorrect (UB). To fix reverse_iterators you will need to use the "pointers to next element trick". – Arnaud Jun 11 '18 at 14:55
  • Arnaud, I just added the pointer member to the custom container class that better shows what I meant. – Enzo Jun 11 '18 at 15:28
  • That `explicit operator bool()` is a really bad idea, please never do that. It makes `while(it++)` look reasonable, but do entirely the wrong thing – Mooing Duck Sep 20 '19 at 17:47
  • In the case of raw pointers, the operator bool() is the equivalent of just checking if a pointer is null. Your "while(it++)" would not work if using a raw pointer since the pointer wouldn't know if it is pointing to a valid address – Enzo Sep 20 '19 at 20:42
  • I think std::iterator is deprecated in c++17 ? – Trung0246 Oct 08 '19 at 02:08
  • @Trung0246, you are correct, i updated it accordingly – Enzo Oct 09 '19 at 23:49
  • 2
    The reverse iterator is work for nothing, since the standard library provides a reverse-iterator adapter. And you failed to make the iterator type assignable from the const iterator. – Toby Speight Dec 10 '19 at 13:07
  • @TobySpeight, you are correct, I did not know about std::reverse_iterator when I wrote this example years ago – Enzo Feb 06 '20 at 23:00
  • perhaps `cbegin()` and other `const_iterator` methods could/should be `const`? – mariusm Aug 24 '20 at 07:39
  • in `blRawIterator`, `operator+` and `operator-` should be `const`, using an implementation like `{ auto temp(*this); temp += distance; return temp; }. – Touloudou Oct 27 '20 at 08:51
29

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.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Actually, you can also do it the other way around by defining a copy constructor that is template, it won't compile if you try to cast the underlying type from `const` to non-`const`. – Matthieu M. Aug 27 '10 at 09:48
  • Won't you end up with an invalid `IntrusiveSlistIterator::operator IntrusiveSlistIterator() const`? – Potatoswatter Aug 27 '10 at 09:56
  • Ah, it is valid, but Comeau gives a warning and I suspect a lot of others will as well. An `enable_if` might fix it, but… – Potatoswatter Aug 27 '10 at 09:59
  • I did not bother with enable_if because the compiler disables it anyway, although some compilers give a warning (g++ being a good boy does not warn). – Maxim Egorushkin Aug 27 '10 at 10:05
  • 1
    @Matthieu: If one goes with a template constructor, when converting const_iterator to iterator the compiler produces an error inside the constructor, making the user scratch his head in confusion and utter wtf. With the conversion operator I posted, the compiler just says that there is no suitable conversion from const_iterator to iterator, which, IMO, is more clear. – Maxim Egorushkin Aug 27 '10 at 10:08
  • @Maxim: I hadn't thought about a better diagnostic, that would be interesting. It would be great if the warning could be fixed because I usually compile with warnings considered as errors, but I'll keep this in mind. – Matthieu M. Aug 27 '10 at 16:31
  • The conversion operator does not fix all the issues issue with member comparison operators. The compiler does not attempt to convert the object you're calling from, so if your first operand is non-const but your second is not, the operator is not defined. – Khaur Dec 17 '14 at 20:02
  • @MaximYegorushkin Take `IntrusiveSlistIterator a;` and `IntrusiveSlistIterator b;` `b==a` is valid, since `a` gets converted to the const version, but `a==b` is not, since the operator requires a non const iterator and cannot convert to it. In the second case, conversion to a const iterator for `a` is not attempted because `operator==` is a member method. – Khaur Dec 18 '14 at 10:42
  • @Khaur You are right, but this has nothing to do with the conversion operators. This is how member comparisons operators work. The fix is trivial - make the member comparison a non-member, see the updated version of the code. – Maxim Egorushkin Dec 18 '14 at 16:21
26

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.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Can you explain the meaning of this comment? `// a private type avoids misuse ` – kevinarpe Feb 07 '17 at 06:39
  • @kevinarpe: `enabler` is never intended to be provider by the caller, so my guess is that they make it private to avoid people accidentally attempting to pass it. I don't think, off hand, that it could create any issue to actually pass it, since the protection lies in `enable_if`. – Matthieu M. Feb 07 '17 at 07:25
  • @orenrevenge: This is copy/pasted from the link, formatting included. Welcome to Boost code... – Matthieu M. Apr 19 '21 at 17:06
18

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;
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Note: it looks like your conversions iterator->const_iterator and back are broken. – Maxim Egorushkin Aug 27 '10 at 09:41
  • @Maxim: Yes, I can't actually find any examples of using my technique :vP . I'm not sure what you mean the conversions are broken, since I simply didn't illustrate them, but there might be an issue accessing `cur` from the iterator of opposite constness. The solution that comes to mind is `friend my_container::const_iterator; friend my_container::iterator;`, but I don't think that's how I did it before… anyway this general outline works. – Potatoswatter Aug 27 '10 at 09:52
  • 1
    * make that `friend class` in both cases. – Potatoswatter Aug 27 '10 at 10:01
  • It's been some time, but I recall now that the conversions should be predicated (by SFINAE) on the well-formedness of the underlying member initializations. This follows the SCARY pattern (but this post predates that terminology). – Potatoswatter Sep 24 '13 at 00:02
14

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.

VinGarcia
  • 1,015
  • 12
  • 19
5

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

};
Keivan
  • 673
  • 7
  • 15
  • With this, you are breaking encapsulation principle of your `Foo` object, by allowing it underlying container to be modified through the iterator without really knowing it. Also, the value to return from the iterator might not be the one saved in the STL container: for example, you might want to provide only a portion of what is really saved internally, or make some transformation before to return it. – Adrian Maire Mar 17 '23 at 17:34
  • Interesting. Had not thought about it from this aspect. Thank you. But all other solutions suffer from the same issue, don't they? Whatever the class `Foo` is, defining any non-const iterator provides unrestricted access to its stored data. – Keivan Mar 18 '23 at 18:50
4

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(); }
}
Treebeard
  • 322
  • 1
  • 9
-3

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;
    }
};