Making my own iterator: how to convert a non-const iterator to a const-iterator?

Hi all,

I am trying to implement a custom container and I need to make an iterator for it. I want to squish const iterator and non-const iterator in one class definition. and here is the issue I have encountered:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<type_traits>

template <typename T, bool is_const = true>           
class node_itr              
{
public:
    typedef typename std::conditional<is_const, node<T>*, node<T>* >::type node_T_ptr;
    node_itr(node_T_ptr d = 0):data(d){}       
    node_itr(node_itr<T,true>& n):data(n.data){}    
//Question: I have to use reference here for converting a non-const iterator to a const iterator, 
//but I cannot do such conversion on rvalues of non-const, such as vec.begin(). 

// ...other utilities are omitted

private:
    node_T_ptr data;
}



I cannot convert a rvalue of non-const node_itr to a const node_itr object, because the compiler requires to use reference& in the copy constructor for this case to avoid recursive conversion! (note: I chose to use a bool parameter in the template so that we only need to change the type of pointer we need for non-const and const iterators.)

Note:

I've read that one can use std::iterator and can go down this path:

(https://stackoverflow.com/questions/3582608/how-to-correctly-implement-custom-iterators-and-const-iterators)

However, I'm just interested in how the wheel was invented and decided to make one by myself :)
Last edited on
> I want to squish const iterator and non-const iterator in one class definition.

Something like this, perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <cassert>

template < typename T > struct slist
{
     void push_front( const T& v ) { first = new node { v, first } ; }

     template < bool CONST > struct iterator_impl ;

     using iterator = iterator_impl<false>;
     iterator begin() { return iterator{first} ; }
     iterator end() { return iterator{nullptr} ; }

     using const_iterator = iterator_impl<true> ;
     const_iterator begin() const { return const_iterator{first} ; }
     const_iterator end() const { return const_iterator{nullptr} ; }
     const_iterator cbegin() const { return begin() ; }
     const_iterator cend() const { return end() ; }

     private:

         struct node
         {
             explicit node( const T& v, node* n ) : value(v), next(n) {}
             T value ;
             node* next ;
         } ;

         node* first = nullptr ;

     public:

     template < bool IS_CONST > struct iterator_impl
     {
         using value_type = typename std::conditional< IS_CONST, const T, T >::type ;

         explicit iterator_impl( node* n ) : curr(n) {}

         value_type& operator* () { return curr->value ; }
         value_type& operator-> () { return std::addressof(**this) ; }

         iterator_impl& operator++ () { curr = curr->next ; return *this ; }
         iterator_impl operator++ (int) { const auto pv = *this ; ++*this ; return pv ; }

         bool operator== ( const_iterator that ) const { return curr == that.curr ; }
         bool operator!= ( const_iterator that ) const { return !( *this == that ) ; }

         // provide for implicit conversion from iterator to const_iterator
         operator const_iterator () const { return const_iterator{curr} ; }

         private: node* curr ;

         friend iterator ; // give iterator access to const_iterator::curr
     };
};

int main()
{
    slist<int> lst ;
    for( int i = 0 ; i < 10 ; ++i ) lst.push_front(i) ;

    for( int& v : lst ) v += 100 ;
    for( int v : lst ) std::cout << v << ' ' ;
    std::cout << '\n' ;

    // test implicit conversions from iterator to const_iterator
    slist<int>::iterator iter = ++++lst.begin() ;
    slist<int>::const_iterator citer = ++++lst.cbegin() ;
    slist<int>::const_iterator citer2 = iter ;

    // test operator ==, operator !=
    assert( iter == iter && iter == citer && citer2 == iter && iter == citer2 && citer == citer2 ) ;
    std::cout << *iter << ' ' << *citer << ' ' << *citer2 << '\n' ;
}

http://coliru.stacked-crooked.com/a/e12cfe2f429e9c8c
https://rextester.com/XGLO59860
Topic archived. No new replies allowed.