0

This is my LinkedList namespace

namespace LinkedList {     
    template<class T>
    class Node{
    public:
        Node(const T& data)
            :data_(data), next_(nullptr) {}
        T data_;
        Node<T> *next_;

        Node<T> *operator++(){
            return next_;
        }
    };


    

    template<class T>
    class LinkedList{
    public:
        LinkedList()
            : head_(nullptr), tail_(nullptr) {}
        
        ~LinkedList(){
            Node<T> *curr = head_;
            Node<T> *next;
            while(curr != nullptr){
                next = curr->next_;
                delete curr;
                curr = next;
            }
        }
        void Append(const T& data) {
            Node<T> *tmp = new Node<T>(data);
            if(head_ == nullptr) {
                head_ = tmp;
                tail_ = tmp;
            } else if(head_ == tail_){
                head_->next_ = tmp;
                tail_ = tmp;
            } else {
                tail_->next_ = tmp;
                tail_ = tmp;
            }
        }

        void Present(){
            for(Node<T> *curr = head_; curr != nullptr; curr=curr->next_){
                std::cout << curr->data_ << std::endl;
            }
        }

        Node<T> *begin(){
            return head_;
        }

        Node<T> *end(){
            return nullptr;
        }
    private:
        Node<T> *head_;
        Node<T> *tail_;
    };
}

I was reading into Iterators and wanted to make my LinkedList object compatible with range based for loops. This is what I got from reading about range-based for loops

for(auto x: list){ }

is equivalent to

for(; begin != end; ++begin) { //*begin};

I thought I was being cheeky and could skip a couple steps of operator overloading (!=, *) for my LinkedList to work with range based for loops by programming my iterator into my Node class for no other reason besides that I felt like it. This is only my second day of learning C++ so this might be a stupid question but I am still a little confused on why this doesn't work:

LinkedList::LinkedList<int> list;
list.Append(3);
list.Append(5);
for(auto x: list) { //blah blah blah}

I know that my prefix increment is the core issue, when the for loop does ++__begin, what I am hoping it does is ___begin=_begin->next however it does not. Why is this? How the operator overloading works in my mind is is that whatever member variables I reference in the overloading function, it is referencing from the instance I am operating on. And when I return the ptr, it is setting whatever instance I am operating on to this ptr. I know my understanding of this is wrong because it doesn't work, so please someone explain to me how it actually works lol.

  • There are 2 separate increment operator, pre-incremenet and post-increment. `++begin` is pre-increment (the ++ goes before the variable) and for an iterator it's supposed to modify itself and return itself. This makes your design of building it into the `Node` class more or less impossible in this scenario. – super Dec 10 '20 at 23:20
  • @super understood, you are incredibly helpful thank you! – killometers90 Dec 10 '20 at 23:23
  • @super so just to be clear, in the overloading function you are actually referencing the instance you are operating on? and because you can't set this = to something else, it is impossible? – killometers90 Dec 10 '20 at 23:26
  • Yes. You want a separate object that just points to the `Node` instance, and when you do `++` on it you modify it to point to the next `Node`. – super Dec 10 '20 at 23:29
  • The return value isn't actually used here. But that's how all the iterators in the standard library works and pretty much anyone using your code is going to expect your iterator to do the same. – super Dec 10 '20 at 23:30
  • Handy reading: [Writing your own STL Container](https://stackoverflow.com/questions/7758580/writing-your-own-stl-container). You might not need all of this, but you need a fair bit of the iterator classes implemented to get range-based `for` working. – user4581301 Dec 10 '20 at 23:34

1 Answers1

1

You've written a

template<class T>
Node<T>* Node<T>::operator++();

which would get invoked like

Node<int> n;
Node<int> *p = ++n; // calls the pre-increment operator

Note this is quite different to the canonical form

Node<T>& Node<T>::operator++();

which returns a reference to the incremented object that you can use in an expression.

But there's another problem: operator overloading works

When an operator appears in an expression, and at least one of its operands has a class type or an enumeration type, ...

But Node<T>* is not a class type. It's a pointer, and pointers already have their own built-in operators. You can't overload them. There is no Node<T>*::operator++().

Write an iterator. It's not that bad, and they actually work!


NB. Iterators are designed to generalize pointers, and to have a compatible interface. You can use raw pointers if your container is basically an array - because incrementing a pointer is already the right way to iterate over an array.

It's only when we want to provide array-like or pointer-like access to a non-contiguous structure that we have to do this extra work.

Useless
  • 64,155
  • 6
  • 88
  • 132