3

I am making a Stack which is based on linked-list. Everything works fine except that I don't know how to implement a "range-based for loop" for this one.

I got the error: no match for ‘operator++’ (operand type is ‘Stack<int>::Node’).

What's wrong and how can I fix it?


Code (Foolish me overloads post ++ not prefix ++, now all corrected.):

#include <iostream>
using namespace std;

template<typename T>
class Stack{
private:
    class Node{
        friend Stack;
    public:
        void operator++(){
            this->next = this->next->next;   //point to next Node
        }

        bool operator!=(const Node& rhs){
            return !(*this == rhs);
        }

        T operator*(){
            return this->next->elem;  //return current Node elem
        }

        bool operator==(const Node& rhs){
            return this->next == rhs.next;
        }

    private:
        T elem;
        Node* next;

    };

    Node* first;
    int _size;

public:
    Stack():_size(0){
        first = nullptr;
    }

    void push(T item){
        Node* n = new Node;
        n->elem = item;
        n->next = first;
        first = n;
        _size++;
    }

    T pop(){
        T item = first->elem;
        Node* old_first = first;
        first = first->next;
        delete old_first;
        _size--;
        return item;
    }

    int size(){
        return _size;
    }

    bool empty(){
        return _size == 0;
    }

    Node begin(){
        Node n;
        n.next = first;
        return n;
    }

    Node end(){
        Node m;
        m.next = nullptr;
        return m;
    }

    ~Stack(){
        Node* ele_to_delete;
        while(first != nullptr){
            ele_to_delete = first;
            first = first->next;
            delete ele_to_delete;
        }
    }
    Stack(const Stack&) = delete;
    Stack& operator=(const Stack&) = delete;
};


int main(){
    Stack<int> ls;
    ls.push(1);
    ls.push(2);
    ls.push(3);
    for(auto s: ls){
        cout << s << "|";
    }
    return 0;
}
Rick
  • 7,007
  • 2
  • 49
  • 79
  • 3
    Note __begin is a reserved identifier because of two underscores. User code should never use such identifiers as it results in undefined behaviour. – n. m. could be an AI Oct 15 '18 at 04:00
  • @n.m. Oh yes, I did not use that. I just post it as a demonstration "equivalent" of the range-based loop. – Rick Oct 15 '18 at 04:03
  • 1
    Normally you should use `list` and `stack` class templates provided by the standard library. If you want to implement your own as an exercise, become familiar with the concept of *iterator* as used by C++ and use it in your implementation – n. m. could be an AI Oct 15 '18 at 04:07
  • @n.m. Yes I am doing it as an exercise of course. I read this post https://stackoverflow.com/questions/8164567/how-to-make-my-custom-type-to-work-with-range-based-for-loops/ and cppreference. Isn't is enough if I implement `begin` and `end` and then overload some operators? – Rick Oct 15 '18 at 04:12
  • 1
    In any case a stack should *only* implement push, pop, top, and is_empty (or count) operations. It should **not** expose begin, end, nodes, or anything range-for-loop related. A list should have all of those though. – n. m. could be an AI Oct 15 '18 at 04:15
  • 1
    I suggest to gain some experience with standard containers and iterators before trying to implement your own containers. – n. m. could be an AI Oct 15 '18 at 04:20
  • @n.m. Wait. The book itself implement this range-based loop in Java. Give me a few mins to upload the images. – Rick Oct 15 '18 at 04:22
  • 1
    Please **DO NOT** upload images of code. They are a useless waste of *my* babdwidth. Thank you. – n. m. could be an AI Oct 15 '18 at 04:24
  • @n.m. Of course I will post it as a comment here. Why being so unfriendly? You are not helping me with the error at all. And that's what the book ask me to do. So every time other people ask a question and you are going to criticize the purpose of doing it? – Rick Oct 15 '18 at 04:29
  • 1
    You post what you want. I won't look at an image of code, linked or otherwise. I also don't care about Java books, in particular those that fail to teach proper coding methods. C++ is not Java. When doing C++ you learn C++ ways. I told you what new C++ concept you should learn in order to implement range-based for. How Java does its range-based for is utterly irrelevant for C++. – n. m. could be an AI Oct 15 '18 at 04:38
  • @n.m. Did you ever run my code and try to help? First, you said I should not use `__begin`. Did I use it? I posted because I want others know that I know what is a range-based loop. Then, you said I should use blablalba. Don't you see this is an exercise? Then you said I should learn the concept of `iterator` and did not say why. I said I've read 2 related posts and I think that would be enough to implement this. – Rick Oct 15 '18 at 04:45
  • Concerning `std::stack` and iteration: In the past, I had rare cases where I needed a stack (e.g. to manage paths in a graph during traversal) but with the option to iterate through its contents (to inspect the path). Finally, I used `std::vector` which can be used like a stack with `back()` (for `top()`), `push_back()` (for `push()`) and `pop_back()` (for `pop()`) which provides, of course, iterators. In case, the rest of `std::vector` API should be "blocked", it can be wrapped into a class with a bunch of inline methods. (Inheriting from std containers is not recommended.) – Scheff's Cat Oct 15 '18 at 05:51
  • 1
    I have read your code, it's not too useful. You have guessed correctly that you need to operate with some kind of class type objects rather than with `Node` pointers. These objects are called iterators in C++, so you probably do want to learn about them after all. I don't think you can learn nearly enough from two SO posts, but go ahead and read them anyway. Good luck. – n. m. could be an AI Oct 15 '18 at 06:05
  • Your Node class is similar to an iterator, what it formally lacks to be eligible for a range-based for is the prefix operator++(). However its logic is broken. Your existing ++ updates next but not elem. You need to fix this in order to put the check mark next to your exercise.You also cannot use it to update your list. – n. m. could be an AI Oct 15 '18 at 06:47
  • @n.m. Thank you. I just realized that I implement the wrong `++` operator. Let me try – Rick Oct 15 '18 at 06:51
  • I missed some stiff in your code because it's hard to read it on the phone. I will write a formal answer soon. – n. m. could be an AI Oct 15 '18 at 07:00

1 Answers1

3

First of all, a Stack should not be traversable at all. It should expose top, pop, push, and is_empty, and that's basically it. But let's forget about it and pretend you want to implement a regular linked list.

In C++ we work with the concept of iterators to manage containers and algorithms, and also the range-based for loop. Formally, in order to be eligible for a range-based for loop, an object needs to implement begin() and end() members (or make unqualified begin(x) and end(x) calls work), and the result of these methods needs to implement operator++, operator* and != comparison. Your Node class almost qualifies, except it implements the wrong kind of operator++ (and its logic is broken, because it never updates elem, but formally it's OK as far as the compilation goes).

A typical list class template implementations from the standard library works in a similar way, except it doesn't expose its version of Node directly. Instead, it exposes a pointer-to-node, wrapped in a special object that implements operator* and operator++ and many other things. This allows for much greater flexibility.

Such small objects that behave almost (or entirely) like pointers are called iterators in C++. Iterators are pervasive throughout the standard library and lots of user code. It's a very important concept that every C++ programmer must learn early on. Any good C++ book or course should cover them.

Here's how a fragment of an iterator-based list class may look like:

template <class T> class List {
     struct Node {
         T elem; 
         ...
     };
     ...
   public:
     class Iterator {
        Node* node;
       public:
        Iterator operator++() { 
          node = node->next; return *this;
        }
        T& operator*() { 
          return node->elem;
        }
        ...
     };

     Iterator begin();
     Iterator end();
};

It is recommended to learn the concept by using standard containers and algorithms that expose iterator-based interfaces.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243