0

I have recently learned concepts like Lvalue & Rvalue references, Universal References, Perfect Forwarding, std::move(), std::forward() etc.

I tried to implement templatized version of Stack (Using Linked List) and was wondering how can I use the above mentioned concepts in my program correctly & efficiently.

I wish to take advantage of move semantics !

Note : I did try to do so myself but it didn't seem to work out correctly! I commented //possible use in my code where I think I can and tried to use the above concepts myself !

I won't be mentioning my version of messy code because I felt it was too wrong. Instead I would appreciate if someone can guide or help me in my original code. Any feedback is appreciated !

#include <iostream>
using namespace std;

template <class U>
struct Stack_Node
{
    U data;
    Stack_Node *next;
    Stack_Node(U val) // possible use
        : data(val), next(nullptr)
    {
        cout << "\nNew Stack_Node Created!\n";
    }
};

template <class T>
class Stack
{
private:
    Stack_Node<T> *head;

public:
    Stack() : head(nullptr) { cout << "\nNew Stack Created!\n"; }
    ~Stack()
    {
        while (head != nullptr)
        {
            Stack_Node<T> *ptr = head;
            head = head->next;
            delete ptr;
        }
    }

    bool empty() { return head == nullptr; }

    size_t size()
    {
        size_t count = 0;
        Stack_Node<T> *ptr = head;
        while (ptr != nullptr)
        {
            ++count;
            ptr = ptr->next;
        }
        return count;
    }

    T &top() { return head->data; }

    void push(T val) // possible use
    {
        Stack_Node<T> *ptr = new Stack_Node<T>(val); // possible use
        ptr->next = head;
        head = ptr;
    }

    void pop()
    {
        if (empty())
            return;
        Stack_Node<T> *ptr = head;
        head = head->next;
        delete ptr;
    }
};

template <class T>
void print(Stack<T> &s)
{
    Stack_Node<T> *ptr = s.head;
    while (ptr != nullptr)
    {
        cout << ptr->data << " ";
        ptr = ptr->next;
    }
}

int main()
{
}

Edit : 1. I could use Rvalue reference like this :-

void push(T &&val) // HERE !!!
{
     Stack_Node<T> *ptr = new Stack_Node<T>(val); // possible use
     ptr->next = head;
     head = ptr;
}

But then I would have to use std::move() when calling the push() function if I wish to pass Lvalues but instead I want my Stack class to remain a blackbox so that the user don't have to be concerned about using the move semantics...and my class should still work in the most efficient way possible by taking some advantage of move semantics .

  • 1
    Agreed. `void push(T val) // possible use` is a good place to use a rvalue reference to take advantage of moving. I recommend adding to the question how you would do it because that is where the really good question is. If you get it right, awesome. You didn't need us. If not, then we have a question we can answer. – user4581301 Dec 20 '21 at 01:10
  • @user4581301 What you are saying is correct that I can do something like `void push(T &&val) // possible use` where I can use Rvalue reference but then I won't be able to pass Lvalues to this function... obviously a solution to this would be to use `std::move()` when calling the function BUT then I would have to use `std::move()` in the `main()` function where I do the calling which I don't want ! I want the my Stack class to remain a BLACK BOX for the user, so that the user don't have to be bothered in using move semantics, everything should be handled automatically behind the scenes. – Pulkit Saini Dec 20 '21 at 06:16
  • @user4581301 Is there a way to achieve that and still take some other advantage of move semantics ? – Pulkit Saini Dec 20 '21 at 06:22
  • ***...if I wish to pass Lvalues but instead I want my Stack class to remain a blackbox...*** It's not possible to support both call by value and call by rvalue - both have same ranking [link1](https://stackoverflow.com/a/31551595) [link2](https://stackoverflow.com/q/36696312) – kiner_shah Dec 20 '21 at 06:57
  • 1
    Close. `Stack_Node *ptr = new Stack_Node(val);` -> `Stack_Node *ptr = new Stack_Node(std::move(val));` so that if `T` supports moving, `val`'s contents will be moved to the `T` instance in the new `Stack_Node`, leaving `val` in a safe-but-unspecified state. – user4581301 Dec 20 '21 at 18:02

0 Answers0