This is my very first implementation of a full-fledged ADT, which could potentially be use-ready. Now, I'm still learning, therefore I would like to ask you what I can do to further improve the following code.
#ifndef LIST_H
#define LIST_H
#include <utility>
#include <type_traits>
#include <stdexcept>
#include <assert.h>
template<typename T>
class List {
class Node {
friend List;
Node *m_next;
typename std::aligned_storage<sizeof(T), alignof(T)>::type m_data;
Node() : m_next(nullptr) {}
Node(const T &data, Node *next) : m_next(next) {
new(&m_data) T(data);
}
};
Node *m_sentinel,
*m_head,
*m_tail;
size_t m_length;
template<bool is_const>
class Iterator_ {
friend List;
using NodeType = typename std::conditional<is_const, const Node*, Node*>::type;
using ValueReferenceType = typename std::conditional<is_const, const T&, T&>::type;
NodeType m_node;
Iterator_(NodeType node) : m_node(node) {}
public:
Iterator_(const List &list, size_t index = 0) : m_node(list.m_head) {
if (index >= list.m_length) throw std::out_of_range("Iteration out of bounds");
operator+(index);
}
Iterator_ &operator++() {
assert(m_node != m_node->m_next);
m_node = m_node->m_next;
return *this;
}
Iterator_ operator++(int) {
Iterator old = *this;
operator++();
return old;
}
Iterator_ &operator+(size_t index) {
for (size_t cur_index = 0; cur_index < index; ++cur_index)
operator++();
return *this;
}
bool operator==(const Iterator_ &cmp) const {
return m_node == cmp.m_node;
}
bool operator!=(const Iterator_ &cmp) const {
return !operator==(cmp);
}
ValueReferenceType &operator*() const {
assert(m_node != m_node->m_next); // Dereferencing a sentinel (end iterator)
return reinterpret_cast<ValueReferenceType>(m_node->m_data);
}
};
using Iterator = Iterator_<false>;
using ConstIterator = Iterator_<true>;
public:
List() : m_sentinel(new Node()), m_head(m_sentinel), m_tail(m_sentinel), m_length(0) {}
List(const List ©) : List() {
append(copy);
}
List(List &&move) : List() {
std::swap(m_sentinel, move.m_sentinel);
std::swap(m_head, move.m_head);
m_tail = move.m_tail;
m_length = move.m_length;
}
virtual ~List() {
clear();
delete m_sentinel;
}
Iterator begin() {
return Iterator(m_head);
}
Iterator end() {
return Iterator(m_sentinel);
}
ConstIterator begin() const {
return ConstIterator(m_head);
}
ConstIterator end() const {
return ConstIterator(m_sentinel);
}
ConstIterator cbegin() const {
return ConstIterator(m_head);
}
ConstIterator cend() const {
return ConstIterator(m_sentinel);
}
List &clear() {
if (m_head != m_sentinel) {
ConstIterator iter = cbegin(),
iter_end = cend();
while (iter != iter_end) {
const Node *node = iter.m_node;
++iter;
delete node;
}
}
m_head = m_tail = m_sentinel;
m_length = 0;
return *this;
}
List &insert(const T &element, size_t index = 0) {
if (index > m_length) {
assert(0);
index = m_length;
}
if (!index) {
m_head = new Node(element, m_head);
if (!m_length) m_tail = m_head; // Our first element, that makes the tail and head the same
} else if (index == m_length - 1) append(element);
else {
Node *previous = Iterator(*this, index - 1).m_node,
*new_node = new Node(element, previous->m_next);
previous->m_next = new_node;
}
++m_length;
return *this;
}
List &append(const T &element) {
Node *old_tail = m_tail;
m_tail = new Node(element, m_sentinel);
if (!m_length) m_head = m_tail; // Our first element, that makes the tail and head the same
else old_tail->m_next = m_tail;
++m_length;
return *this;
}
List &append(const List ©) {
for (const T &v : copy) append(v);
return *this;
}
List &remove(size_t index) {
if (index >= m_length) throw std::out_of_range("Removal out of bounds");
if (!index) {
Node *next = m_head->m_next;
delete m_head;
m_head = next;
} else {
Iterator iter(*this, index - 1);
Node *previous = iter.m_node,
*rem = previous->m_next;
previous->m_next = rem->m_next;
delete rem;
}
--m_length;
return *this;
}
List &reverse() {
if (m_length < 2) return *this;
Node *previous = nullptr,
*next = nullptr,
*cur = m_head;
while (cur != m_sentinel) {
next = cur->m_next;
if (cur != m_head) cur->m_next = previous;
previous = cur;
cur = next;
}
std::swap(m_head, m_tail);
m_tail->m_next = m_sentinel;
return *this;
}
bool operator==(const List &cmp) const {
return m_head == cmp.m_head;
}
bool operator!=(const List &cmp) const {
return !operator==(cmp);
}
T &operator[](size_t index) const {
return *Iterator(*this, index);
}
List &operator=(const List ©) {
clear();
append(copy);
return *this;
}
size_t size() const {
return m_length;
}
};
#endif