I have implemented a basic stack just for fun. I've just started learning about move constructors and assignment and move semantics as a whole, so I would really appreciate some feedback as to whether I've done it right, and if not, some tips as to how I should do it would be most welcome!
I'm also following the example set by the STL, so I'm trying to build the class in an STL-like manner.
The stack
class is in a different namespace, which I've removed from this example, so conflicts with std::stack
will not appear.
The _stack_node
is also in a different details
namespace, so it is not exposed to the user.
#include <iostream>
#include <cstdlib>
#include <stdexcept>
#include <initializer_list>
#include <cstddef>
#include <utility>
//FORWARD DECLARATION
template<class T>
class stack;
template<class T>
class _stack_node
{
private:
using node = _stack_node<T>;
friend class stack<T>;
private:
T _key;
node *_next;
public:
//COPY CONTRUCTORS
constexpr _stack_node()
: _key(0), _next(nullptr)
{}
constexpr _stack_node(const T &new_key)
: _key(new_key), _next(nullptr)
{}
constexpr _stack_node(const T &new_key, node *new_node)
: _key(new_key), _next(new_node)
{}
//MOVE CONSTRUCTORs
constexpr _stack_node(T &&new_key)
: _key(std::forward<T>(new_key)), _next(nullptr)
{}
constexpr _stack_node(T &&new_key, node *new_node)
: _key(std::forward<T>(new_key)), _next(new_node)
{}
//DESTRUCTOR
~_stack_node() {}
//COPY ASSIGNMENT
node &operator=(const node &rhs)
{
this->_key = rhs._key;
this->_next = rhs._next;
return *this;
}
//MOVE ASSIGNMENT
node &operator=(node &&rhs)
{
this->_key = std::move(rhs._key);
this->_next = std::move(rhs._next);
return *this;
}
};
template<class T>
class stack
{
private:
using node = _stack_node<T>;
public:
using value_type = T;
using size_type = std::size_t;
using reference = T&;
using const_reference = const T&;
private:
node *_top;
std::size_t _size;
private:
void _throw_stack_empty() const
{ //in case we try to pop an empty stack!!
try
{
if (this->_top == nullptr)
throw std::out_of_range("You tried to pop an empty stack!"
" Why the hell would you do that?");
}
catch (const std::out_of_range &rang)
{
std::cerr << "blew::stack: " << rang.what();
std::abort();
}
}
void _add_node(const T &key)
{
node *new_node = new node(key, this->_top);
this->_top = new_node;
++this->_size; //increase size
}
void _delete_node()
{
this->_throw_stack_empty(); //check and throw if stack is empty
node *temp_top = this->_top;
this->_top = this->_top->_next;
delete temp_top;
--this->_size; //decrease size
}
void _clear_all_nodes()
{
while (this->_top != nullptr)
{
node *temp = this->_top;
this->_top = this->_top->_next;
delete temp;
}
this->_size = 0;
}
public:
//COPY CONSTRUCTORS
constexpr stack()
: _top(nullptr), _size(0)
{}
stack(const std::initializer_list<T> &ilist)
{
for (const auto &el : ilist)
this->_copy_add_node(el);
}
stack(const stack &rhs)
{
node *temp = rhs._top;
while (temp != nullptr)
{
this->_copy_add_node(temp->_key);
temp = temp->_next;
}
}
//MOVE CONSTRUCTORS
stack(std::initializer_list<T> &&ilist)
{
for (const auto &el : ilist)
this->_move_add_node(std::move(el));
}
stack(stack &&rhs)
{
node *temp = std::move(rhs._top);
while (temp != nullptr)
{
this->_move_add_node(std::move(temp->_key));
temp = temp->_next;
}
}
//DESTRUCTOR
~stack() { this->_clear_all_nodes(); }
//OPERATIONS
void push(const T &key) { this->_add_node(key); }
void push(T &&key) { this->_add_node(std::move(key)); }
template<class... Args>
void emplace(Args&&... args) { this->_add_node(std::forward<Args>(args)...); }
void pop() { this->_delete_node(); }
//ELEMENT ACCESS
reference top() { return this->_top->_key; }
const_reference top() const { return this->_top->_key; }
//CAPACITY
constexpr bool empty() const { return this->_size == 0 ? true : false; }
constexpr size_type size() const { return this->_size; }
void clear() { this->_delete_all_nodes(); }
//COPY ASSIGNMENT
stack operator=(const stack &rhs)
{
this->_clear_all_nodes();
node *temp = rhs._top;
while (temp != nullptr)
{
this->_add_node(temp->_key);
temp = temp->_next;
}
return *this;
}
stack operator=(const std::initializer_list<T> &ilist)
{
this->_clear_all_nodes();
for (const auto &el : ilist)
this->_add_node(el);
return *this;
}
//MOVE ASSIGNMENT
stack operator=(stack &&rhs)
{
this->_clear_all_nodes();
node *temp = std::move(rhs._top);
while (temp != nullptr)
{
this->_add_node(std::move(temp->_key));
temp = temp->_next;
}
return *this;
}
stack operator=(std::initializer_list<T> &&ilist)
{
this->_clear_all_nodes();
for (const auto &el : ilist)
this->_add_node(std::move(el));
return *this;
}
};