#ifndef __GBLIBCPP_LIST__ #define __GBLIBCPP_LIST__ #include #include #include #include #include #include #include namespace std { template > class list { private: struct node_base { node_base* prev; node_base* next; constexpr node_base() noexcept : prev { this }, next { this } {} constexpr void connect(node_base* _next) noexcept { this->next = _next; _next->prev = static_cast(this); } constexpr void swap(node_base& other) noexcept { std::swap(prev, other.prev); std::swap(next, other.next); } }; struct node : public node_base { T value; template explicit constexpr node(Args&&... args) : value(std::forward(args)...) { } }; public: template class _iterator; public: using value_type = T; using allocator_type = Allocator; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reference = T&; using const_reference = const T&; using pointer = typename std::allocator_traits::pointer; using const_pointer = typename std::allocator_traits::const_pointer; using iterator = _iterator; using const_iterator = _iterator; private: using alloc_traits = std::allocator_traits; using node_alloc_type = typename std::allocator_traits::template rebind_alloc; using node_alloc_traits = std::allocator_traits; public: template class _iterator { public: using const_node_pointer = const node_base*; using node_pointer = node_base*; using value_type = std::conditional_t; using pointer = std::add_pointer_t; using reference = std::add_lvalue_reference_t; friend class list; private: node_pointer p; public: constexpr _iterator() noexcept = default; explicit constexpr _iterator(const_node_pointer p) : p { const_cast(p) } {} constexpr _iterator(const _iterator& iter) noexcept = default; constexpr _iterator(_iterator&& iter) noexcept = default; constexpr ~_iterator() = default; constexpr _iterator& operator=(const _iterator& iter) noexcept = default; constexpr _iterator& operator=(_iterator&& iter) noexcept = default; constexpr bool operator==(const _iterator& iter) const noexcept = default; constexpr reference operator*() const noexcept { return ((node*)p)->value; } constexpr pointer operator&() const noexcept { return std::addressof(this->operator*()); } constexpr pointer operator->() const noexcept { return this->operator&(); } constexpr _iterator& operator++() noexcept { p = p->next; return *this; } constexpr _iterator operator++(int) noexcept { _iterator ret(p); (void)this->operator++(); return ret; } constexpr _iterator& operator--(void) noexcept { p = p->prev; return *this; } constexpr _iterator operator--(int) noexcept { _iterator ret(p); (void)this->operator--(); return ret; } constexpr operator bool() const { return p; } constexpr operator _iterator() const { return _iterator { p }; } }; private: node_base m_head; impl::compressed_pair m_pair; private: // move m_head and m_size of other to *this // other MUST NOT be empty, *this MUST be empty constexpr void _move_from(list&& other) noexcept { std::swap(_size(), other._size()); other.m_head.prev->connect(&m_head); m_head.connect(other.m_head.next); other.m_head.next = other.m_head.prev = &other.m_head; } constexpr size_type& _size() noexcept { return m_pair.first(); } constexpr const size_type& _size() const noexcept { return m_pair.first(); } constexpr node_alloc_type& _alloc() noexcept { return m_pair.second(); } constexpr const node_alloc_type& _alloc() const noexcept { return m_pair.second(); } public: __GBLIBCPP_CONSTEXPR iterator end(void) noexcept { return iterator { &m_head }; } __GBLIBCPP_CONSTEXPR const_iterator cend(void) const noexcept { return const_iterator { &m_head }; } __GBLIBCPP_CONSTEXPR const_iterator end(void) const noexcept { return cend(); } __GBLIBCPP_CONSTEXPR iterator begin(void) noexcept { return iterator { m_head.next }; } __GBLIBCPP_CONSTEXPR const_iterator cbegin(void) const noexcept { return const_iterator { m_head.next }; } __GBLIBCPP_CONSTEXPR const_iterator begin(void) const noexcept { return cbegin(); } template __GBLIBCPP_CONSTEXPR iterator emplace(const_iterator pos, Args&&... args) { node* nd = node_alloc_traits::allocate(_alloc(), 1); node_alloc_traits::construct(_alloc(), nd, std::forward(args)...); nd->next = pos.p; nd->prev = pos.p->prev; nd->next->prev = nd; nd->prev->next = nd; ++_size(); return iterator { nd }; } explicit __GBLIBCPP_CONSTEXPR list(const Allocator& alloc): m_head{}, m_pair{0, alloc} { } __GBLIBCPP_CONSTEXPR list(): list(Allocator()) {} __GBLIBCPP_CONSTEXPR explicit list(size_type count, const Allocator& alloc = Allocator()): list(alloc) { while (count--) emplace_back(); } __GBLIBCPP_CONSTEXPR list(size_type count, const T& value, const Allocator& alloc = Allocator()): list(alloc) { while (count--) emplace_back(value); } template __GBLIBCPP_CONSTEXPR list(InputIter first, InputIter last, const Allocator& alloc = Allocator()) : list(alloc) { insert(first, last); } __GBLIBCPP_CONSTEXPR list(const list& other, const Allocator& alloc): list(alloc) { // TODO: select_on_container_copy_construction for (const auto& item : other) emplace_back(item); } __GBLIBCPP_CONSTEXPR list(const list& other): list(other, alloc_traits::select_on_container_copy_construction(_alloc())) { } __GBLIBCPP_CONSTEXPR list(list&& other): m_head{}, m_pair{0, std::move(other._alloc())} { if (other.empty()) return; _move_from(std::move(other)); } __GBLIBCPP_CONSTEXPR list(list&& other, const Allocator& alloc): m_head{}, m_pair{0, alloc} { if (other._alloc() != alloc) { for (auto iter = other.begin(); iter != other.end(); ++iter) emplace(cend(), std::move(*iter)); other.clear(); return; } // other._alloc() == alloc if (other.empty()) return; _move_from(std::move(other)); } __GBLIBCPP_CONSTEXPR list(std::initializer_list ilist, const Allocator& alloc = Allocator()) : list(alloc) { for (const auto& item : ilist) emplace_back(item); } __GBLIBCPP_CONSTEXPR ~list() { clear(); m_head.next = m_head.prev = nullptr; } __GBLIBCPP_CONSTEXPR list& operator=(const list& other) { // TODO: reuse memory if _alloc() == other._alloc() clear(); if constexpr (alloc_traits:: propagate_on_container_copy_assignment::value) _alloc() = other._alloc(); for (const auto& item : other) emplace_back(item); return *this; } __GBLIBCPP_CONSTEXPR list& operator=(list&& other) { if (alloc_traits:: propagate_on_container_move_assignment::value) { clear(); _alloc() = std::move(other._alloc()); if (other.empty()) return *this; _move_from(std::move(other)); return *this; } // TODO: reuse memory if _alloc() == other._alloc() clear(); if (_alloc() != other._alloc()) { for (auto iter = other.begin(); iter != other.end(); ++iter) emplace(cend(), std::move(*iter)); other.clear(); return *this; } _move_from(std::move(other)); return *this; } __GBLIBCPP_CONSTEXPR list& operator=(std::initializer_list ilist) { // TODO: reuse memory clear(); for (const auto& item : ilist) emplace_back(item); return *this; } __GBLIBCPP_CONSTEXPR void assign(size_type count, const T& value) { clear(); while (count--) emplace_back(value); } template __GBLIBCPP_CONSTEXPR void assign(InputIter first, InputIter last) { clear(); insert(first, last); } __GBLIBCPP_CONSTEXPR void assign(std::initializer_list ilist) { clear(); for (const auto& item : ilist) emplace_back(item); } __GBLIBCPP_CONSTEXPR allocator_type get_allocator() const noexcept { return _alloc(); } __GBLIBCPP_CONSTEXPR reference front() { return *begin(); } __GBLIBCPP_CONSTEXPR const_reference front() const { return *cbegin(); } __GBLIBCPP_CONSTEXPR reference back() { return *--end(); } __GBLIBCPP_CONSTEXPR const_reference back() const { return *--cend(); } __GBLIBCPP_CONSTEXPR iterator insert(const_iterator pos, const T& value) { return emplace(pos, value); } __GBLIBCPP_CONSTEXPR iterator insert(const_iterator pos, T&& value) { return emplace(pos, std::move(value)); } __GBLIBCPP_CONSTEXPR iterator insert(const_iterator pos, size_type count, const T& value) { if (!(count--)) return pos; auto ret = insert(pos, value); while (count--) insert(pos, value); return ret; } template __GBLIBCPP_CONSTEXPR void insert(InputIter first, InputIter last) { for ( ; first != last; ++first) emplace_back(*first); } template __GBLIBCPP_CONSTEXPR reference emplace_back(Args&&... args) { return *emplace(end(), std::forward(args)...); } template __GBLIBCPP_CONSTEXPR reference emplace_front(Args&&... args) { return *emplace(begin(), std::forward(args)...); } __GBLIBCPP_CONSTEXPR void push_back(const T& value) { emplace_back(value); } __GBLIBCPP_CONSTEXPR void push_back(T&& value) { emplace_back(std::move(value)); } __GBLIBCPP_CONSTEXPR void push_front(const T& value) { emplace_front(value); } __GBLIBCPP_CONSTEXPR void push_front(T&& value) { emplace_front(std::move(value)); } __GBLIBCPP_CONSTEXPR void pop_back() { erase(--end()); } __GBLIBCPP_CONSTEXPR void pop_front() { erase(begin()); } __GBLIBCPP_CONSTEXPR iterator erase(const_iterator pos) noexcept { iterator ret { pos.p->next }; pos.p->next->prev = pos.p->prev; pos.p->prev->next = pos.p->next; node_alloc_traits::destroy(_alloc(), (node*)pos.p); node_alloc_traits::deallocate(_alloc(), (node*)pos.p, 1); --_size(); return ret; } __GBLIBCPP_CONSTEXPR iterator erase(const_iterator first, const_iterator last) noexcept { while (first != last) first = erase(first); return first; } __GBLIBCPP_CONSTEXPR void clear() noexcept { for (auto iter = begin(); iter != end(); ) iter = erase(iter); } __GBLIBCPP_CONSTEXPR size_type size() const noexcept { return _size(); } __GBLIBCPP_CONSTEXPR bool empty() const noexcept { return size() == 0; } __GBLIBCPP_CONSTEXPR void swap(list& other) { if constexpr (alloc_traits::propagate_on_container_swap::value) std::swap(_alloc(), other._alloc()); std::swap(_size(), other.m_size); std::swap(m_head, other.m_head); std::swap(m_head.next->prev, other.m_head.next->prev); std::swap(m_head.prev->next, other.m_head.prev->next); } __GBLIBCPP_CONSTEXPR void resize(size_type count) { while (count > size()) emplace_back(); while (count < size()) pop_back(); } __GBLIBCPP_CONSTEXPR void resize(size_type count, const value_type& value) { while (count > size()) emplace_back(value); while (count < size()) pop_back(); } __GBLIBCPP_CONSTEXPR size_type remove(const T& value) { size_type retval = 0; for (auto iter = begin(); iter != end(); ) { if (value != *iter) { ++iter; continue; } ++retval; iter = erase(iter); } return retval; } template __GBLIBCPP_CONSTEXPR size_type remove_if(UnaryPredicate p) { size_type retval = 0; for (auto iter = begin(); iter != end(); ) { if (!p(*iter)) { ++iter; continue; } ++retval; iter = erase(iter); } return retval; } }; template void swap(std::list& lhs, std::list& rhs) { lhs.swap(rhs); } template typename std::list::size_type erase(std::list& l, const U& value) { return l.remove_if([&](auto& elem) { return elem == value; }); } template typename std::list::size_type erase_if(std::list& l, Predicate p) { return l.remove_if(p); } } // namespace std #endif