Home > Software engineering >  implementing a cpp const iterator crashes and burns
implementing a cpp const iterator crashes and burns

Time:12-31

Many posts about const iterators (example), but none in the context of loops like:

for (const auto it: container) { ... }

When I started implementing, I was encouraged by the compiler's complaints about missing begin and end, hoping such complaints can guide me to fill in the missing pieces. I was wrong. The following code compiles fine though it surely lacks the equivalent of both operator and operator!=:

#include <iostream>
template<class T> class List { public:
    const T *head;
    const List<T> *tail;
    List(const T *h, const List<T> *t):head(h),tail(t){}
    const T *operator*() const { return head; } };

template<class T> const List<T> *begin(const List<T> *l) { return l; }
template<class T> const List<T> *end  (const List<T> *l) { return nullptr; }

class Person { public: int age; Person(int a):age(a){} };
typedef List<Person> Persons;

int main(int argc, char **argv) {
    Person *p1 = new Person(16);
    Person *p2 = new Person(27);
    Person *p3 = new Person(38);

    Persons *persons = new Persons(p1,
        new Persons(p2,
        new Persons(p3, nullptr)));

    for (const auto p: persons) { std::cout << (*p)->age << "\n"; }
    return 0; }

How come this code compiles?

CodePudding user response:

You have not defined and !=, but they are perfectly well-defined for your iterator type, const List<T>*, because is a pointer type. However, the default behavior of does not do what you want, which would be to follow the tail pointer.

To imbue your iterator with special knowledge of how should be implemented, you would want to use a separate, custom iterator class, rather than using a pointer.

I also made some other changes to your code:

  • The List type is made iterable, not List*. That means the begin and end functions are not defined on pointers, but on the List object itself, and we iterate over *persons rather than persons.
  • In for (const auto p : *persons), p is a Person, not a pointer.

godbolt.org link

#include <iostream>
template<class T> class List { public:
    const T *head;
    const List<T> *tail;
    List(const T *h, const List<T> *t):head(h),tail(t){}
    const T *operator*() const { return head; }

    class const_iterator {
        const List<T>* cur = nullptr;
    public:
        explicit const_iterator(const List<T>* list) : cur(list) {}
        const_iterator& operator  () {
            cur = cur->tail;
            return *this;
        }
        bool operator!=(const const_iterator& other) const {
            return cur != other.cur;
        }
        const T& operator*() const {
            return *cur->head;
        }
    };

    const_iterator begin() const {
        return const_iterator(this);
    }

    const_iterator end() const {
        return const_iterator(nullptr);
    }
};

class Person { public: int age; Person(int a):age(a){} };
typedef List<Person> Persons;

int main(int argc, char **argv) {
    Person *p1 = new Person(16);
    Person *p2 = new Person(27);
    Person *p3 = new Person(38);

    Persons *persons = new Persons(p1,
        new Persons(p2,
        new Persons(p3, nullptr)));

    for (const auto p: *persons) {
        std::cout << p.age << "\n";
    }
    return 0;
} 
  • Related