#include <iostream>
using namespace std;
template <typename T>
class List
{
struct Node
{
T data;
Node* prev = nullptr;
Node*next = nullptr;
Node() = default;
Node(T d) : data(d), prev(nullptr), next(nullptr) {}
};
Node* head = nullptr;
Node *tail = nullptr;
std::size_t l_size = 0;
//SFINAE
template <typename Iter>
using required_input_iterator = std::enable_if<std::is_base_of_v<std::input_iterator_tag,
typename std::iterator_traits<Iter>::iterator_category >>;
public:
using reference = T&;
using const_reference = const T&;
using size_type = std::size_t;
class const_iterator
{
Node* node = nullptr;
friend class List;
const_iterator (Node* p) : node(p) {}
public:
using value_type = std::remove_reference_t<T>;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = std::bidirectional_iterator_tag;
public:
const_iterator () = default;
const_iterator(const const_iterator& other) : node(other.node) {}
reference operator*()
{
return node->data;
}
T* operator->()
{
return node->data;
}
// prefix operators
const_iterator& operator++()
{
node = node->next;
return *this;
}
const_iterator& operator--()
{
node = node->prev;
return *this;
}
// postfix operators
const_iterator operator++(int)
{
Node* temp = this->node;
node = node->next;
return temp;
}
const_iterator operator--(int)
{
Node* temp = this->node;
node = node->prev;
return temp;
}
friend bool operator== (const const_iterator& lhs, const const_iterator& rhs) noexcept
{
return lhs.node == rhs.node;
}
friend bool operator!= (const const_iterator& lhs, const const_iterator& rhs) noexcept
{
return lhs.node != rhs.node;
}
};
class iterator : public std::input_iterator_tag
{
Node * node = nullptr;
friend class List;
iterator(Node* p) : node(p) {}
public:
using value_type = std::remove_reference_t<T>;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = std::bidirectional_iterator_tag;
public:
iterator () = default;
reference operator*()
{
return node->data;
}
reference operator->()
{
return node->data;
}
// prefix operators
iterator& operator++()
{
node = node->next;
return *this;
}
iterator& operator--()
{
node = node->prev;
return *this;
}
// postfix operators
iterator operator++(int)
{
Node* temp = this->node;
node = node->next;
return temp;
}
iterator operator--(int)
{
Node* temp = this->node;
node = node->prev;
return temp;
}
friend bool operator== (const iterator& lhs, const iterator& rhs) noexcept
{
return lhs.node == rhs.node;
}
friend bool operator!= (const iterator& lhs, const iterator& rhs) noexcept
{
return lhs.node != rhs.node;
}
};
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
List() = default;
explicit List(size_type n);
List(size_type n, const_reference v);
List(const std::initializer_list<T>& i_list);
template<typename InputIterator, typename = required_input_iterator<InputIterator>>
List(InputIterator first, InputIterator last);
List(const List& src);
List& operator=(List src);
List(List&& src);
//~List();
void assign(size_type n, const_reference v);
void assign(const std::initializer_list<T>& i_list);
template<typename InputIterator, typename = decltype(*std::declval<InputIterator>())>
void assign(InputIterator first, InputIterator last);
// element acces
reference front();
const_reference front() const;
reference back();
const_reference back() const;
// iterators
iterator begin() noexcept
{
return iterator(head);
}
const_iterator begin() const noexcept
{
return iterator(head);
}
iterator end() noexcept
{
return iterator(tail);
}
const_iterator end() const noexcept
{
return iterator(tail);
}
const_iterator cbegin() const noexcept;
const_iterator cend() const;
reverse_iterator rbegin() noexcept;
const_reverse_iterator crbegin() const noexcept;
reverse_iterator rend() noexcept;
const_reverse_iterator crend() const noexcept;
// capacity
size_type size() const noexcept;
bool empty() const noexcept;
// modifiers
void clear() noexcept;
iterator insert(const_iterator pos, const_reference v);
iterator insert(const_iterator pos, const T&& v);
iterator insert(const_iterator pos, size_type n, const_reference v);
template<typename InputIterator, typename = decltype(*std::declval<InputIterator>())> iterator insert(InputIterator first, InputIterator last);
iterator insert(const_iterator pos, std::initializer_list<T> i_list);
template<typename... Args> iterator emplace(const iterator& pos, Args&&... args );
iterator erase(const_iterator pos);
iterator erase(const_iterator first, const_iterator last);
void push_back(const_reference v);
void push_back(T&& v);
template<typename... Args> reference emplace_back(Args&&...args);
void pop_back();
void push_front(const_reference v);
void push_front(T&& v);
template<typename... Args> reference emplace_front(Args&&...args);
void pop_front();
void resize(size_type new_size);
void resize(size_type new_size, const_reference v);
void swap(List& other) noexcept;
};
template <typename T>
List<T>::List(size_type n)
{
if (n == 0)
return;
head = new Node();
Node* temp = head;
temp->prev = nullptr;
for(int i = 0; i < n; ++i)
{
temp->next = new Node(T());
temp->next->prev = temp;
temp = temp->next;
}
tail = temp->next;
}
template <typename T>
List<T>::List(size_type n, const_reference v) : l_size(n)
{
if (n == 0)
return;
Node* temp = new Node(v);
head = temp;
temp->prev = nullptr;
for(int i = 0; i < n; ++i)
{
temp->next = new Node(v);
temp->next->prev = temp;
temp = temp->next;
}
tail = temp;
}
template <typename T>
List<T>::List(const std::initializer_list<T>& i_list) : l_size(i_list.size())
{
if(l_size == 0)
return;
Node* temp = new Node;
head = temp;
temp->prev = nullptr;
for(auto it = i_list.begin(); it != i_list.end(); ++it)
{
temp->data = *it;
temp->next = new Node;
temp->next->prev = temp;
temp = temp->next;
}
tail = temp;
}
template<typename T>
List<T>::List(const List& other) : l_size(other.l_size)
{
if(l_size == 0)
return;
Node* temp = other.head;
Node* node = new Node(temp->data);
head = node;
node->prev = nullptr;
temp = temp->next;
int i = 0;
while(temp->next)
{
node->next = new Node(temp->data);
node->next->prev = node;
node = node->next;
temp = temp->next;
}
tail = node->next;
}
template<typename T>
template<typename... Args>
typename List<T>::iterator List<T>::emplace(const iterator& pos, Args&&... args )
{
Node* curr = pos.node;
Node* new_node = new Node(std::move(args)...);
// this is where I am having a really hard time
if(curr == tail)
{
curr = new Node(std::move(args)...);
curr->next = nullptr;
tail = curr->next;
}
else
{
curr->prev = new_node;
new_node->next = curr;
head = new_node;
}
}
int main()
{
List<int> lis({1, 2, 3, 4, 5, 6});
List<int> li(4);
for(auto e: li)
{
cout << e << ' ';
}
cout << '\n';
List<int> l1 = lis;
l1.emplace(l1.begin(), 78); // this lne changes nothing
for(auto e: l1)
{
cout << e << ' ';
}
}