#include <iterator>
#include <type_traits>
#include <iostream>
#include <vector>
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
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:
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->next;
return temp;
}
const_iterator operator--(int)
{
Node* temp = *this;
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
{
// Define your iterator traits however you like here
// These members will be used by std::iterator_traits for SFINAE
public:
// what values that my iterator accept?
using value_type = std::remove_reference_t<T>;
// What is the difference type of 'Node' struct?
using difference_type = std::ptrdiff_t;
// What is the pointer type of this iterator?
using pointer = value_type*;
// What is the reference type of this iterator?
using reference = value_type&;
// What is the category of this iterator?
// What kinds of iterators shall our List class accept?
using iterator_category = std::input_iterator_tag;
// Uncomment bellow line to accept ex. std::vector iterator
// using iterator_category = std::random_access_iterator_tag
Node* node = nullptr;
friend class List;
iterator(Node* p) : node(p)
{
}
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->next;
return temp;
}
iterator operator--(int)
{
Node* temp = *this;
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;
}
};
template<typename InputIterator>
using required_input_iterator =
std::enable_if_t<std::is_same_v<
typename std::iterator_traits<InputIterator>::iterator_category,
typename std::iterator_traits<List<T>::iterator>::iterator_category>>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
List() = default;
explicit List(size_type n)
{
std::cout << "List(size_type n)" << std::endl;
}
List(size_type n, const_reference v)
{
std::cout << "List(size_type n, const_reference v)" << std::endl;
}
List(const std::initializer_list<T>& i_list)
{
std::cout << "List(const std::initializer_list<T>& i_list)" << std::endl;
}
template<typename InputIterator, typename = required_input_iterator<InputIterator>>
List(InputIterator, InputIterator)
{
std::cout << "List(InputIterator, InputIterator)" << std::endl;
}
List(const List& src);
List& operator=(const List& src);
List(const List&& src);
//~List();
void assign(size_type n, const_reference v);
void assign(const std::initializer_list<T>& i_list);
template<typename 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> 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<T> other) noexcept;
};
int main()
{
// TEST CASE: List(size_type n, const_reference v)
List<int> lis(5, 6);
// TEST CASE: List(InputIterator, InputIterator)
List<int> iter_list(lis.begin(), lis.end());
//std::iterator_traits<List<int>::iterator>::value_type s;
// TEST CASE: with vector this doesn't work because vector is random access iterator
// If you want to accept vector iterator then define your iterator as follows:
// using iterator_category = std::random_access_iterator_tag
std::vector<int> vec_lis{ 1,2,3,4,5 };
// List<int> vec_lis_test(vec_lis.begin(), vec_lis.end()); // ERROR: my iterator is input iter not random!
// TEST CASE: explicit List(size_type n)
List<int> asdf(10);
// TEST CASE: List(const std::initializer_list<T>& i_list)
List<int> int_list({ 1,2,3,4,5,6 });
int arr[] {1,2,3,4,5};
List<int> arr_list(arr, arr+5);
}