What's wrong with my Iterator?

Here I'm trying to implement a simple forward iterator working with arrays. All works fine but the -> operator. What is missed for getting the -> to work?
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
#include <string>
#include <iostream>

/* Just for testing proposes.
 */
struct My_class
{
    std::string speak() { return "I'm from a MyClass object."; }
};


/* A simple forward-iterator, working for arrays.
 */
template <typename T>
class Iterator
{
    T * m_hook;  // points to date
public:
     Iterator<T>( T * hook )
     : m_hook{hook}
     {}

     T& operator*() { return * m_hook; }  // If this works, then should also work the '->'.
     const T& operator*() const { return * m_hook; }
          
     void operator++() { ++m_hook;}
     
     bool operator!=( const Iterator<T> other) const { return m_hook != other.m_hook; }
};


/* Interface for build-in arrays.
 */
template <typename T>
class Array_ref
{
private:
    std::size_t m_size;
    T * m_startptr;   // points the first array value
    Iterator<T> first;
    Iterator<T> last;
public:
    Array_ref( T * start, const std::size_t size)
    : m_startptr{start}, m_size{size}
    , first{ Iterator<T>(start)}
    , last{ Iterator<T>(start+size)}
    {}
    
    T& operator[]( const std::size_t idx )
    { return m_startptr[idx]; }
    
    std::size_t size() const
    { return m_size; }
    
    Iterator<T> begin() { return first; }
    Iterator<T> end() { return last; }
};


/* Testing the Array_ref interface and the Iterator.
 */
int main()
{
    My_class v[10];   // simple array
    Array_ref<My_class> av(v, 10);   // the array gets an interface
    for (auto it = av.begin(); it != av.end(); ++ it) *it = My_class(); // Initializing values.
    
    for (auto & val : av) std::cout << val.speak();  // Testing with range_for
    
    // Testing the -> operator
    Iterator<My_class> it = v;
   // std::cout << it->speak();   // The -> doesn't work!
}
> [operator*] If this works, then should also work the '->'.
no, it doesn't work like that.
you need to overload the -> operator.
you need to overload the -> operator.

Thanks. But sadly I have no idea how to implement the operator->() at my Iterator.
T *operator->() { return m_hook;}
OP: you've undertaken an ambitious project - user-defined iterator && user-defined container - regarding the user-defined iterator recall that
(i)terators are objects that can iterator over elements of a sequence via a common interface that is adapted from ordinary pointers.
- from 'The C++ Standard Library (2nd ed)' by N Josuttis, 9.2
So in your program the iterators should ideally be templated over the container and not the underlying data as you've done. Furthermore, user-defined iterators should inherit from one of the corresponding iterator traits (in your case forward_iterator_tag) - the advantage of doing this it that it ensures that an iterator provides all type definition which include iterator_category, value_type, difference_type, pointer, reference. The last three are optional and have default values. These type defintions are missing in your program.
Just focussing on the user-defined iterator for now, here's what a rudimentary implementation might look like:
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
# include <iostream>
# include <iterator>
# include <vector>

template <typename Container>
class simple_forward_iterator
    : public std::iterator<std::forward_iterator_tag, typename Container::value_type>
{
    protected:
        Container& m_container;

    public:
        explicit simple_forward_iterator (Container& c) : m_container(c){}

        simple_forward_iterator<Container>& operator= (const typename Container::value_type& value)
        {
            m_container.push_back(value);
            return *this;
        }
        simple_forward_iterator<Container>& operator * ()
        {
            return *this;
        }
        simple_forward_iterator<Container>& operator -> ()
        {
            return *this;
        }
        simple_forward_iterator<Container>& operator++ ()
        {
            return *this;
        }
        simple_forward_iterator<Container>& operator++ (int)
        {
            return *this;
        }
};
template <typename Container>
inline simple_forward_iterator<Container> create_forward_iterator (Container& c)
{
    return simple_forward_iterator<Container>(c);
}
struct My_class
{
    int m_num;
    My_class(int num) : m_num(num){}
    void speak() const { std::cout << "I'm MyClass object " << m_num << "\n"; }
};

int main()
{
    std::vector<My_class> my_vec;

    simple_forward_iterator<decltype(my_vec)> iter(my_vec);

    *iter = My_class(1);
    iter++;
    *iter = My_class(2);
    iter++;
    *iter = My_class(3);

    for (const auto& elem : my_vec)
    {
        elem.speak();
    }
}

Above is an adaptation from section 9.6 (Writing User-Defined Iterators) of the above mentioned book and if you wish to pursue this topic I'd recommend strongly you take a look at this book both for user-defined iterators and containers that are standard library compatible
> iterators should ideally be templated over the container and not the underlying data as you've done.

This is an unequivocably asinine statement.


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 <string>
#include <iterator>

// A simple forward-iterator, working for arrays (academic exercise).
template < typename T >
class Iterator // : public std::iterator< std::forward_iterator_tag, T > (discouraged by C++17)
{
    T* m_hook ;  // points to item

    public:

        // The requirements of a standard-library compatible iterator includes:
        // std::iterator_traits<Iterator> has the member typedefs value_type,
        //           difference_type, reference, pointer, and iterator_category
        // see: http://en.cppreference.com/w/cpp/concept/Iterator
        //      http://en.cppreference.com/w/cpp/iterator/iterator_traits

        // one way to implement this in C++14 was to inherit publicly from std::iterator<>
        // for example, for this iterator, from std::iterator< std::forward_iterator_tag, T >
        // for good reasons, this is discouraged by C++17 (is it deprecated? cppreference states that it is)
        // so we just provide the necessary typedefs in our iterator class
        using iterator_category	= std::forward_iterator_tag ;
        using value_type = T ;
        using pointer = T* ;
        using reference = T& ;
        using difference_type = std::ptrdiff_t ;

        Iterator<T>( T* hook = nullptr ) : m_hook{hook} {} // default constructible

        // note: it is the iterator that is is const ie. *iter does not modify iter
        // the returned reference may or may not be a reference to const
        // depending on what T is. for Iterator<int>, the result is int&
        // and for Iterator< const int > the result is const int&
        T& operator*() const { return *m_hook; }


        // idiomatic: operator-> returns the address of the result of operator*
        // we need to maintain only operator*. operator-> keeps itself in sync
        T* operator-> () const { return std::addressof(**this) ; }

        Iterator& operator++() { ++m_hook; return *this ; } // prefix increment

        // requirement: both ++iter and iter++ must be supported
        // postfix increment is typically implemented in terms of prefix increment
        // make a copy of the old value; call pthe prefix increment,
        //         and return the before-increment copy as a prvalue
        // we need to maintain only operator++(). operator++(int) keeps itself in sync
        Iterator operator++(int) { auto old_val{*this}; return old_val ; } // postfix increment

        // requirement: both equality and inequality comparison must be defined
        bool operator== ( const Iterator<T>& other ) const { return m_hook == other.m_hook; }

        // operator!= can be implemented as the negation of operator==
        // we need to maintain only operator==. operator!= keeps itself in sync
        bool operator!= ( const Iterator<T>& other ) const { return !( *this == other ); }
};

template < typename T, std::size_t N > Iterator<T> my_begin( T(&arr)[N] ) { return arr ; }
template < typename T, std::size_t N > Iterator<T> my_end( T(&arr)[N] ) { return arr+N ; }

int main()
{
    struct My_class
    {
        int id ;
        My_class( int id = 0 ) : id(id) {}
        std::string speak() const { return "MyClass{" + std::to_string(id) + '}' ; }
    };

    const My_class arr[] { 0, 1, 2, 3, 4, 5, 6, 7 } ;
    for( auto iter = my_begin(arr) ; iter != my_end(arr) ; ++iter )
        std::cout << iter->speak() << '\n' ;
}

http://coliru.stacked-crooked.com/a/1b21830933f27d26
Last edited on
This is an unequivocably asinine statement.

hah! the 'great' JLBorges strikes again. Adept at C++ with the emotional maturity of a toddler.
Gratuitous abuse continues ... http://www.cplusplus.com/forum/beginner/214047/#msg1004157
Last edited on
It's just awesome what effort some members lay in their posts. Thanks.
Topic archived. No new replies allowed.