/////////////////////////////////////////////////////////////////////////////// // Copyright (c) Electronic Arts Inc. All rights reserved. /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // This file implements a doubly-linked list, much like the C++ std::list class. // The primary distinctions between this list and std::list are: // - list doesn't implement some of the less-frequently used functions // of std::list. Any required functions can be added at a later time. // - list has a couple extension functions that increase performance. // - list can contain objects with alignment requirements. std::list cannot // do so without a bit of tedious non-portable effort. // - list has optimizations that don't exist in the STL implementations // supplied by library vendors for our targeted platforms. // - list supports debug memory naming natively. // - list::size() by default is not a constant time function, like the list::size // in some std implementations such as STLPort and SGI STL but unlike the // list in Dinkumware and Metrowerks. The EASTL_LIST_SIZE_CACHE option can change this. // - list provides a guaranteed portable node definition that allows users // to write custom fixed size node allocators that are portable. // - list is easier to read, debug, and visualize. // - list is savvy to an environment that doesn't have exception handling, // as is sometimes the case with console or embedded environments. // - list has less deeply nested function calls and allows the user to // enable forced inlining in debug builds in order to reduce bloat. // - list doesn't keep a member size variable. This means that list is // smaller than std::list (depends on std::list) and that for most operations // it is faster than std::list. However, the list::size function is slower. // - list::size_type is defined as eastl_size_t instead of size_t in order to // save memory and run faster on 64 bit systems. /////////////////////////////////////////////////////////////////////////////// #ifndef EASTL_LIST_H #define EASTL_LIST_H #include #include #include #include #include #include #include EA_DISABLE_ALL_VC_WARNINGS() #include #include EA_RESTORE_ALL_VC_WARNINGS() // 4530 - C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc // 4345 - Behavior change: an object of POD type constructed with an initializer of the form () will be default-initialized // 4571 - catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. // 4623 - default constructor was implicitly defined as deleted EA_DISABLE_VC_WARNING(4530 4345 4571 4623); #if defined(EA_PRAGMA_ONCE_SUPPORTED) #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. #endif namespace eastl { /// EASTL_LIST_DEFAULT_NAME /// /// Defines a default container name in the absence of a user-provided name. /// #ifndef EASTL_LIST_DEFAULT_NAME #define EASTL_LIST_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " list" // Unless the user overrides something, this is "EASTL list". #endif /// EASTL_LIST_DEFAULT_ALLOCATOR /// #ifndef EASTL_LIST_DEFAULT_ALLOCATOR #define EASTL_LIST_DEFAULT_ALLOCATOR allocator_type(EASTL_LIST_DEFAULT_NAME) #endif /// ListNodeBase /// /// We define a ListNodeBase separately from ListNode (below), because it allows /// us to have non-templated operations such as insert, remove (below), and it /// makes it so that the list anchor node doesn't carry a T with it, which would /// waste space and possibly lead to surprising the user due to extra Ts existing /// that the user didn't explicitly create. The downside to all of this is that /// it makes debug viewing of a list harder, given that the node pointers are of /// type ListNodeBase and not ListNode. However, see ListNodeBaseProxy below. /// struct ListNodeBase { ListNodeBase* mpNext; ListNodeBase* mpPrev; void insert(ListNodeBase* pNext) EA_NOEXCEPT; // Inserts this standalone node before the node pNext in pNext's list. void remove() EA_NOEXCEPT; // Removes this node from the list it's in. Leaves this node's mpNext/mpPrev invalid. void splice(ListNodeBase* pFirst, ListNodeBase* pLast) EA_NOEXCEPT; // Removes [pFirst,pLast) from the list it's in and inserts it before this in this node's list. void reverse() EA_NOEXCEPT; // Reverses the order of nodes in the circular list this node is a part of. static void swap(ListNodeBase& a, ListNodeBase& b) EA_NOEXCEPT; // Swaps the nodes a and b in the lists to which they belong. void insert_range(ListNodeBase* pFirst, ListNodeBase* pFinal) EA_NOEXCEPT; // Differs from splice in that first/final aren't in another list. static void remove_range(ListNodeBase* pFirst, ListNodeBase* pFinal) EA_NOEXCEPT; // } EASTL_LIST_PROXY_MAY_ALIAS; #if EASTL_LIST_PROXY_ENABLED /// ListNodeBaseProxy /// /// In debug builds, we define ListNodeBaseProxy to be the same thing as /// ListNodeBase, except it is templated on the parent ListNode class. /// We do this because we want users in debug builds to be able to easily /// view the list's contents in a debugger GUI. We do this only in a debug /// build for the reasons described above: that ListNodeBase needs to be /// as efficient as possible and not cause code bloat or extra function /// calls (inlined or not). /// /// ListNodeBaseProxy *must* be separate from its parent class ListNode /// because the list class must have a member node which contains no T value. /// It is thus incorrect for us to have one single ListNode class which /// has mpNext, mpPrev, and mValue. So we do a recursive template trick in /// the definition and use of SListNodeBaseProxy. /// template struct ListNodeBaseProxy { LN* mpNext; LN* mpPrev; }; template struct ListNode : public ListNodeBaseProxy< ListNode > { T mValue; }; #else EA_DISABLE_VC_WARNING(4625 4626) template struct ListNode : public ListNodeBase { T mValue; }; EA_RESTORE_VC_WARNING() #endif /// ListIterator /// template struct ListIterator { typedef ListIterator this_type; typedef ListIterator iterator; typedef ListIterator const_iterator; typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t. typedef ptrdiff_t difference_type; typedef T value_type; typedef ListNode node_type; typedef Pointer pointer; typedef Reference reference; typedef EASTL_ITC_NS::bidirectional_iterator_tag iterator_category; public: node_type* mpNode; public: ListIterator() EA_NOEXCEPT; ListIterator(const ListNodeBase* pNode) EA_NOEXCEPT; ListIterator(const iterator& x) EA_NOEXCEPT; this_type next() const EA_NOEXCEPT; this_type prev() const EA_NOEXCEPT; reference operator*() const EA_NOEXCEPT; pointer operator->() const EA_NOEXCEPT; this_type& operator++() EA_NOEXCEPT; this_type operator++(int) EA_NOEXCEPT; this_type& operator--() EA_NOEXCEPT; this_type operator--(int) EA_NOEXCEPT; }; // ListIterator /// ListBase /// /// See VectorBase (class vector) for an explanation of why we /// create this separate base class. /// template class ListBase { public: typedef T value_type; typedef Allocator allocator_type; typedef ListNode node_type; typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t. typedef ptrdiff_t difference_type; #if EASTL_LIST_PROXY_ENABLED typedef ListNodeBaseProxy< ListNode > base_node_type; #else typedef ListNodeBase base_node_type; // We use ListNodeBase instead of ListNode because we don't want to create a T. #endif protected: eastl::compressed_pair mNodeAllocator; #if EASTL_LIST_SIZE_CACHE size_type mSize; #endif base_node_type& internalNode() EA_NOEXCEPT { return mNodeAllocator.first(); } base_node_type const& internalNode() const EA_NOEXCEPT { return mNodeAllocator.first(); } allocator_type& internalAllocator() EA_NOEXCEPT { return mNodeAllocator.second(); } const allocator_type& internalAllocator() const EA_NOEXCEPT { return mNodeAllocator.second(); } public: const allocator_type& get_allocator() const EA_NOEXCEPT; allocator_type& get_allocator() EA_NOEXCEPT; void set_allocator(const allocator_type& allocator); protected: ListBase(); ListBase(const allocator_type& a); ~ListBase(); node_type* DoAllocateNode(); void DoFreeNode(node_type* pNode); void DoInit() EA_NOEXCEPT; void DoClear(); }; // ListBase /// list /// /// -- size() is O(n) -- /// Note that as of this writing, list::size() is an O(n) operation when EASTL_LIST_SIZE_CACHE is disabled. /// That is, getting the size of the list is not a fast operation, as it requires traversing the list and /// counting the nodes. We could make list::size() be fast by having a member mSize variable. There are reasons /// for having such functionality and reasons for not having such functionality. We currently choose /// to not have a member mSize variable as it would add four bytes to the class, add a tiny amount /// of processing to functions such as insert and erase, and would only serve to improve the size /// function, but no others. The alternative argument is that the C++ standard states that std::list /// should be an O(1) operation (i.e. have a member size variable), most C++ standard library list /// implementations do so, the size is but an integer which is quick to update, and many users /// expect to have a fast size function. The EASTL_LIST_SIZE_CACHE option changes this. /// To consider: Make size caching an optional template parameter. /// /// Pool allocation /// If you want to make a custom memory pool for a list container, your pool /// needs to contain items of type list::node_type. So if you have a memory /// pool that has a constructor that takes the size of pool items and the /// count of pool items, you would do this (assuming that MemoryPool implements /// the Allocator interface): /// typedef list WidgetList; // Delare your WidgetList type. /// MemoryPool myPool(sizeof(WidgetList::node_type), 100); // Make a pool of 100 Widget nodes. /// WidgetList myList(&myPool); // Create a list that uses the pool. /// template class list : public ListBase { typedef ListBase base_type; typedef list this_type; public: typedef T value_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef ListIterator iterator; typedef ListIterator const_iterator; typedef eastl::reverse_iterator reverse_iterator; typedef eastl::reverse_iterator const_reverse_iterator; typedef typename base_type::size_type size_type; typedef typename base_type::difference_type difference_type; typedef typename base_type::allocator_type allocator_type; typedef typename base_type::node_type node_type; typedef typename base_type::base_node_type base_node_type; using base_type::mNodeAllocator; using base_type::DoAllocateNode; using base_type::DoFreeNode; using base_type::DoClear; using base_type::DoInit; using base_type::get_allocator; #if EASTL_LIST_SIZE_CACHE using base_type::mSize; #endif using base_type::internalNode; using base_type::internalAllocator; public: list(); list(const allocator_type& allocator); explicit list(size_type n, const allocator_type& allocator = EASTL_LIST_DEFAULT_ALLOCATOR); list(size_type n, const value_type& value, const allocator_type& allocator = EASTL_LIST_DEFAULT_ALLOCATOR); list(const this_type& x); list(const this_type& x, const allocator_type& allocator); list(this_type&& x); list(this_type&&, const allocator_type&); list(std::initializer_list ilist, const allocator_type& allocator = EASTL_LIST_DEFAULT_ALLOCATOR); template list(InputIterator first, InputIterator last); // allocator arg removed because VC7.1 fails on the default arg. To do: Make a second version of this function without a default arg. this_type& operator=(const this_type& x); this_type& operator=(std::initializer_list ilist); this_type& operator=(this_type&& x); // In the case that the two containers' allocators are unequal, swap copies elements instead // of replacing them in place. In this case swap is an O(n) operation instead of O(1). void swap(this_type& x); void assign(size_type n, const value_type& value); template // It turns out that the C++ std::list specifies a two argument void assign(InputIterator first, InputIterator last); // version of assign that takes (int size, int value). These are not // iterators, so we need to do a template compiler trick to do the right thing. void assign(std::initializer_list ilist); iterator begin() EA_NOEXCEPT; const_iterator begin() const EA_NOEXCEPT; const_iterator cbegin() const EA_NOEXCEPT; iterator end() EA_NOEXCEPT; const_iterator end() const EA_NOEXCEPT; const_iterator cend() const EA_NOEXCEPT; reverse_iterator rbegin() EA_NOEXCEPT; const_reverse_iterator rbegin() const EA_NOEXCEPT; const_reverse_iterator crbegin() const EA_NOEXCEPT; reverse_iterator rend() EA_NOEXCEPT; const_reverse_iterator rend() const EA_NOEXCEPT; const_reverse_iterator crend() const EA_NOEXCEPT; bool empty() const EA_NOEXCEPT; size_type size() const EA_NOEXCEPT; void resize(size_type n, const value_type& value); void resize(size_type n); reference front(); const_reference front() const; reference back(); const_reference back() const; template void emplace_front(Args&&... args); template void emplace_back(Args&&... args); void push_front(const value_type& value); void push_front(value_type&& x); reference push_front(); void* push_front_uninitialized(); void push_back(const value_type& value); void push_back(value_type&& x); reference push_back(); void* push_back_uninitialized(); void pop_front(); void pop_back(); template iterator emplace(const_iterator position, Args&&... args); iterator insert(const_iterator position); iterator insert(const_iterator position, const value_type& value); iterator insert(const_iterator position, value_type&& x); iterator insert(const_iterator position, std::initializer_list ilist); iterator insert(const_iterator position, size_type n, const value_type& value); template iterator insert(const_iterator position, InputIterator first, InputIterator last); iterator erase(const_iterator position); iterator erase(const_iterator first, const_iterator last); reverse_iterator erase(const_reverse_iterator position); reverse_iterator erase(const_reverse_iterator first, const_reverse_iterator last); void clear() EA_NOEXCEPT; void reset_lose_memory() EA_NOEXCEPT; // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. void remove(const T& x); template void remove_if(Predicate); void reverse() EA_NOEXCEPT; // splice inserts elements in the range [first,last) before position and removes the elements from x. // In the case that the two containers' allocators are unequal, splice copies elements // instead of splicing them. In this case elements are not removed from x, and iterators // into the spliced elements from x continue to point to the original values in x. void splice(const_iterator position, this_type& x); void splice(const_iterator position, this_type& x, const_iterator i); void splice(const_iterator position, this_type& x, const_iterator first, const_iterator last); void splice(const_iterator position, this_type&& x); void splice(const_iterator position, this_type&& x, const_iterator i); void splice(const_iterator position, this_type&& x, const_iterator first, const_iterator last); public: // For merge, see notes for splice regarding the handling of unequal allocators. void merge(this_type& x); void merge(this_type&& x); template void merge(this_type& x, Compare compare); template void merge(this_type&& x, Compare compare); void unique(); template void unique(BinaryPredicate); // Sorting functionality // This is independent of the global sort algorithms, as lists are // linked nodes and can be sorted more efficiently by moving nodes // around in ways that global sort algorithms aren't privy to. void sort(); template void sort(Compare compare); public: bool validate() const; int validate_iterator(const_iterator i) const; protected: node_type* DoCreateNode(); template node_type* DoCreateNode(Args&&... args); template void DoAssign(Integer n, Integer value, true_type); template void DoAssign(InputIterator first, InputIterator last, false_type); void DoAssignValues(size_type n, const value_type& value); template void DoInsert(ListNodeBase* pNode, Integer n, Integer value, true_type); template void DoInsert(ListNodeBase* pNode, InputIterator first, InputIterator last, false_type); void DoInsertValues(ListNodeBase* pNode, size_type n, const value_type& value); template void DoInsertValue(ListNodeBase* pNode, Args&&... args); void DoErase(ListNodeBase* pNode); void DoSwap(this_type& x); template iterator DoSort(iterator i1, iterator end2, size_type n, Compare& compare); }; // class list /////////////////////////////////////////////////////////////////////// // ListNodeBase /////////////////////////////////////////////////////////////////////// // Swaps the nodes a and b in the lists to which they belong. This is similar to // splicing a into b's list and b into a's list at the same time. // Works by swapping the members of a and b, and fixes up the lists that a and b // were part of to point to the new members. inline void ListNodeBase::swap(ListNodeBase& a, ListNodeBase& b) EA_NOEXCEPT { const ListNodeBase temp(a); a = b; b = temp; if(a.mpNext == &b) a.mpNext = a.mpPrev = &a; else a.mpNext->mpPrev = a.mpPrev->mpNext = &a; if(b.mpNext == &a) b.mpNext = b.mpPrev = &b; else b.mpNext->mpPrev = b.mpPrev->mpNext = &b; } // splices the [first,last) range from its current list into our list before this node. inline void ListNodeBase::splice(ListNodeBase* first, ListNodeBase* last) EA_NOEXCEPT { // We assume that [first, last] are not within our list. last->mpPrev->mpNext = this; first->mpPrev->mpNext = last; this->mpPrev->mpNext = first; ListNodeBase* const pTemp = this->mpPrev; this->mpPrev = last->mpPrev; last->mpPrev = first->mpPrev; first->mpPrev = pTemp; } inline void ListNodeBase::reverse() EA_NOEXCEPT { ListNodeBase* pNode = this; do { EA_ANALYSIS_ASSUME(pNode != NULL); ListNodeBase* const pTemp = pNode->mpNext; pNode->mpNext = pNode->mpPrev; pNode->mpPrev = pTemp; pNode = pNode->mpPrev; } while(pNode != this); } inline void ListNodeBase::insert(ListNodeBase* pNext) EA_NOEXCEPT { mpNext = pNext; mpPrev = pNext->mpPrev; pNext->mpPrev->mpNext = this; pNext->mpPrev = this; } // Removes this node from the list that it's in. Assumes that the // node is within a list and thus that its prev/next pointers are valid. inline void ListNodeBase::remove() EA_NOEXCEPT { mpNext->mpPrev = mpPrev; mpPrev->mpNext = mpNext; } // Inserts the standalone range [pFirst, pFinal] before pPosition. Assumes that the // range is not within a list and thus that it's prev/next pointers are not valid. // Assumes that this node is within a list and thus that its prev/next pointers are valid. inline void ListNodeBase::insert_range(ListNodeBase* pFirst, ListNodeBase* pFinal) EA_NOEXCEPT { mpPrev->mpNext = pFirst; pFirst->mpPrev = mpPrev; mpPrev = pFinal; pFinal->mpNext = this; } // Removes the range [pFirst, pFinal] from the list that it's in. Assumes that the // range is within a list and thus that its prev/next pointers are valid. inline void ListNodeBase::remove_range(ListNodeBase* pFirst, ListNodeBase* pFinal) EA_NOEXCEPT { pFinal->mpNext->mpPrev = pFirst->mpPrev; pFirst->mpPrev->mpNext = pFinal->mpNext; } /////////////////////////////////////////////////////////////////////// // ListIterator /////////////////////////////////////////////////////////////////////// template inline ListIterator::ListIterator() EA_NOEXCEPT : mpNode() // To consider: Do we really need to intialize mpNode? { // Empty } template inline ListIterator::ListIterator(const ListNodeBase* pNode) EA_NOEXCEPT : mpNode(static_cast((ListNode*)const_cast(pNode))) // All this casting is in the name of making runtime debugging much easier on the user. { // Empty } template inline ListIterator::ListIterator(const iterator& x) EA_NOEXCEPT : mpNode(const_cast(x.mpNode)) { // Empty } template inline typename ListIterator::this_type ListIterator::next() const EA_NOEXCEPT { return ListIterator(mpNode->mpNext); } template inline typename ListIterator::this_type ListIterator::prev() const EA_NOEXCEPT { return ListIterator(mpNode->mpPrev); } template inline typename ListIterator::reference ListIterator::operator*() const EA_NOEXCEPT { return mpNode->mValue; } template inline typename ListIterator::pointer ListIterator::operator->() const EA_NOEXCEPT { return &mpNode->mValue; } template inline typename ListIterator::this_type& ListIterator::operator++() EA_NOEXCEPT { mpNode = static_cast(mpNode->mpNext); return *this; } template inline typename ListIterator::this_type ListIterator::operator++(int) EA_NOEXCEPT { this_type temp(*this); mpNode = static_cast(mpNode->mpNext); return temp; } template inline typename ListIterator::this_type& ListIterator::operator--() EA_NOEXCEPT { mpNode = static_cast(mpNode->mpPrev); return *this; } template inline typename ListIterator::this_type ListIterator::operator--(int) EA_NOEXCEPT { this_type temp(*this); mpNode = static_cast(mpNode->mpPrev); return temp; } // The C++ defect report #179 requires that we support comparisons between const and non-const iterators. // Thus we provide additional template paremeters here to support this. The defect report does not // require us to support comparisons between reverse_iterators and const_reverse_iterators. template inline bool operator==(const ListIterator& a, const ListIterator& b) EA_NOEXCEPT { return a.mpNode == b.mpNode; } template inline bool operator!=(const ListIterator& a, const ListIterator& b) EA_NOEXCEPT { return a.mpNode != b.mpNode; } // We provide a version of operator!= for the case where the iterators are of the // same type. This helps prevent ambiguity errors in the presence of rel_ops. template inline bool operator!=(const ListIterator& a, const ListIterator& b) EA_NOEXCEPT { return a.mpNode != b.mpNode; } /////////////////////////////////////////////////////////////////////// // ListBase /////////////////////////////////////////////////////////////////////// template inline ListBase::ListBase() : mNodeAllocator(base_node_type(), allocator_type(EASTL_LIST_DEFAULT_NAME)) #if EASTL_LIST_SIZE_CACHE , mSize(0) #endif { DoInit(); } template inline ListBase::ListBase(const allocator_type& allocator) : mNodeAllocator(base_node_type(), allocator) #if EASTL_LIST_SIZE_CACHE , mSize(0) #endif { DoInit(); } template inline ListBase::~ListBase() { DoClear(); } template const typename ListBase::allocator_type& ListBase::get_allocator() const EA_NOEXCEPT { return internalAllocator(); } template typename ListBase::allocator_type& ListBase::get_allocator() EA_NOEXCEPT { return internalAllocator(); } template inline void ListBase::set_allocator(const allocator_type& allocator) { EASTL_ASSERT((internalAllocator() == allocator) || (static_cast(internalNode().mpNext) == &internalNode())); // We can only assign a different allocator if we are empty of elements. internalAllocator() = allocator; } template inline typename ListBase::node_type* ListBase::DoAllocateNode() { node_type* pNode = (node_type*)allocate_memory(internalAllocator(), sizeof(node_type), EASTL_ALIGN_OF(T), 0); EASTL_ASSERT(pNode != nullptr); return pNode; } template inline void ListBase::DoFreeNode(node_type* p) { EASTLFree(internalAllocator(), p, sizeof(node_type)); } template inline void ListBase::DoInit() EA_NOEXCEPT { internalNode().mpNext = (ListNode*)&internalNode(); internalNode().mpPrev = (ListNode*)&internalNode(); } template inline void ListBase::DoClear() { node_type* p = static_cast(internalNode().mpNext); while(p != &internalNode()) { node_type* const pTemp = p; p = static_cast(p->mpNext); pTemp->~node_type(); EASTLFree(internalAllocator(), pTemp, sizeof(node_type)); } } /////////////////////////////////////////////////////////////////////// // list /////////////////////////////////////////////////////////////////////// template inline list::list() : base_type() { // Empty } template inline list::list(const allocator_type& allocator) : base_type(allocator) { // Empty } template inline list::list(size_type n, const allocator_type& allocator) : base_type(allocator) { DoInsertValues((ListNodeBase*)&internalNode(), n, value_type()); } template inline list::list(size_type n, const value_type& value, const allocator_type& allocator) : base_type(allocator) { DoInsertValues((ListNodeBase*)&internalNode(), n, value); } template inline list::list(const this_type& x) : base_type(x.internalAllocator()) { DoInsert((ListNodeBase*)&internalNode(), const_iterator((ListNodeBase*)x.internalNode().mpNext), const_iterator((ListNodeBase*)&x.internalNode()), false_type()); } template inline list::list(const this_type& x, const allocator_type& allocator) : base_type(allocator) { DoInsert((ListNodeBase*)&internalNode(), const_iterator((ListNodeBase*)x.internalNode().mpNext), const_iterator((ListNodeBase*)&x.internalNode()), false_type()); } template inline list::list(this_type&& x) : base_type(eastl::move(x.internalAllocator())) { swap(x); } template inline list::list(this_type&& x, const allocator_type& allocator) : base_type(allocator) { swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. } template inline list::list(std::initializer_list ilist, const allocator_type& allocator) : base_type(allocator) { DoInsert((ListNodeBase*)&internalNode(), ilist.begin(), ilist.end(), false_type()); } template template list::list(InputIterator first, InputIterator last) : base_type(EASTL_LIST_DEFAULT_ALLOCATOR) { //insert(const_iterator((ListNodeBase*)&internalNode()), first, last); DoInsert((ListNodeBase*)&internalNode(), first, last, is_integral()); } template typename list::iterator inline list::begin() EA_NOEXCEPT { return iterator((ListNodeBase*)internalNode().mpNext); } template inline typename list::const_iterator list::begin() const EA_NOEXCEPT { return const_iterator((ListNodeBase*)internalNode().mpNext); } template inline typename list::const_iterator list::cbegin() const EA_NOEXCEPT { return const_iterator((ListNodeBase*)internalNode().mpNext); } template inline typename list::iterator list::end() EA_NOEXCEPT { return iterator((ListNodeBase*)&internalNode()); } template inline typename list::const_iterator list::end() const EA_NOEXCEPT { return const_iterator((ListNodeBase*)&internalNode()); } template inline typename list::const_iterator list::cend() const EA_NOEXCEPT { return const_iterator((ListNodeBase*)&internalNode()); } template inline typename list::reverse_iterator list::rbegin() EA_NOEXCEPT { return reverse_iterator((ListNodeBase*)&internalNode()); } template inline typename list::const_reverse_iterator list::rbegin() const EA_NOEXCEPT { return const_reverse_iterator((ListNodeBase*)&internalNode()); } template inline typename list::const_reverse_iterator list::crbegin() const EA_NOEXCEPT { return const_reverse_iterator((ListNodeBase*)&internalNode()); } template inline typename list::reverse_iterator list::rend() EA_NOEXCEPT { return reverse_iterator((ListNodeBase*)internalNode().mpNext); } template inline typename list::const_reverse_iterator list::rend() const EA_NOEXCEPT { return const_reverse_iterator((ListNodeBase*)internalNode().mpNext); } template inline typename list::const_reverse_iterator list::crend() const EA_NOEXCEPT { return const_reverse_iterator((ListNodeBase*)internalNode().mpNext); } template inline typename list::reference list::front() { #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED if (EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) EASTL_FAIL_MSG("list::front -- empty container"); #else // We allow the user to reference an empty container. #endif return static_cast(internalNode().mpNext)->mValue; } template inline typename list::const_reference list::front() const { #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED if (EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) EASTL_FAIL_MSG("list::front -- empty container"); #else // We allow the user to reference an empty container. #endif return static_cast(internalNode().mpNext)->mValue; } template inline typename list::reference list::back() { #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED if (EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) EASTL_FAIL_MSG("list::back -- empty container"); #else // We allow the user to reference an empty container. #endif return static_cast(internalNode().mpPrev)->mValue; } template inline typename list::const_reference list::back() const { #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED if (EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) EASTL_FAIL_MSG("list::back -- empty container"); #else // We allow the user to reference an empty container. #endif return static_cast(internalNode().mpPrev)->mValue; } template inline bool list::empty() const EA_NOEXCEPT { #if EASTL_LIST_SIZE_CACHE return (mSize == 0); #else return static_cast(internalNode().mpNext) == &internalNode(); #endif } template inline typename list::size_type list::size() const EA_NOEXCEPT { #if EASTL_LIST_SIZE_CACHE return mSize; #else #if EASTL_DEBUG const ListNodeBase* p = (ListNodeBase*)internalNode().mpNext; size_type n = 0; while(p != (ListNodeBase*)&internalNode()) { ++n; p = (ListNodeBase*)p->mpNext; } return n; #else // The following optimizes to slightly better code than the code above. return (size_type)eastl::distance(const_iterator((ListNodeBase*)internalNode().mpNext), const_iterator((ListNodeBase*)&internalNode())); #endif #endif } template typename list::this_type& list::operator=(const this_type& x) { if(this != &x) // If not assigning to self... { // If (EASTL_ALLOCATOR_COPY_ENABLED == 1) and the current contents are allocated by an // allocator that's unequal to x's allocator, we need to reallocate our elements with // our current allocator and reallocate it with x's allocator. If the allocators are // equal then we can use a more optimal algorithm that doesn't reallocate our elements // but instead can copy them in place. #if EASTL_ALLOCATOR_COPY_ENABLED bool bSlowerPathwayRequired = (internalAllocator() != x.internalAllocator()); #else bool bSlowerPathwayRequired = false; #endif if(bSlowerPathwayRequired) { clear(); #if EASTL_ALLOCATOR_COPY_ENABLED internalAllocator() = x.internalAllocator(); #endif } DoAssign(x.begin(), x.end(), eastl::false_type()); } return *this; } template typename list::this_type& list::operator=(this_type&& x) { if(this != &x) { clear(); // To consider: Are we really required to clear here? x is going away soon and will clear itself in its dtor. swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. } return *this; } template typename list::this_type& list::operator=(std::initializer_list ilist) { DoAssign(ilist.begin(), ilist.end(), false_type()); return *this; } template inline void list::assign(size_type n, const value_type& value) { DoAssignValues(n, value); } // It turns out that the C++ std::list specifies a two argument // version of assign that takes (int size, int value). These are not // iterators, so we need to do a template compiler trick to do the right thing. template template inline void list::assign(InputIterator first, InputIterator last) { DoAssign(first, last, is_integral()); } template inline void list::assign(std::initializer_list ilist) { DoAssign(ilist.begin(), ilist.end(), false_type()); } template inline void list::clear() EA_NOEXCEPT { DoClear(); DoInit(); #if EASTL_LIST_SIZE_CACHE mSize = 0; #endif } template inline void list::reset_lose_memory() EA_NOEXCEPT { // The reset_lose_memory function is a special extension function which unilaterally // resets the container to an empty state without freeing the memory of // the contained objects. This is useful for very quickly tearing down a // container built into scratch memory. DoInit(); #if EASTL_LIST_SIZE_CACHE mSize = 0; #endif } template void list::resize(size_type n, const value_type& value) { iterator current((ListNodeBase*)internalNode().mpNext); size_type i = 0; while((current.mpNode != &internalNode()) && (i < n)) { ++current; ++i; } if(i == n) erase(current, (ListNodeBase*)&internalNode()); else insert((ListNodeBase*)&internalNode(), n - i, value); } template inline void list::resize(size_type n) { resize(n, value_type()); } template template void list::emplace_front(Args&&... args) { DoInsertValue((ListNodeBase*)internalNode().mpNext, eastl::forward(args)...); } template template void list::emplace_back(Args&&... args) { DoInsertValue((ListNodeBase*)&internalNode(), eastl::forward(args)...); } template inline void list::push_front(const value_type& value) { DoInsertValue((ListNodeBase*)internalNode().mpNext, value); } template inline void list::push_front(value_type&& value) { emplace(begin(), eastl::move(value)); } template inline typename list::reference list::push_front() { node_type* const pNode = DoCreateNode(); ((ListNodeBase*)pNode)->insert((ListNodeBase*)internalNode().mpNext); #if EASTL_LIST_SIZE_CACHE ++mSize; #endif return static_cast(internalNode().mpNext)->mValue; // Same as return front(); } template inline void* list::push_front_uninitialized() { node_type* const pNode = DoAllocateNode(); ((ListNodeBase*)pNode)->insert((ListNodeBase*)internalNode().mpNext); #if EASTL_LIST_SIZE_CACHE ++mSize; #endif return &pNode->mValue; } template inline void list::pop_front() { #if EASTL_ASSERT_ENABLED if(EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) EASTL_FAIL_MSG("list::pop_front -- empty container"); #endif DoErase((ListNodeBase*)internalNode().mpNext); } template inline void list::push_back(const value_type& value) { DoInsertValue((ListNodeBase*)&internalNode(), value); } template inline void list::push_back(value_type&& value) { emplace(end(), eastl::move(value)); } template inline typename list::reference list::push_back() { node_type* const pNode = DoCreateNode(); ((ListNodeBase*)pNode)->insert((ListNodeBase*)&internalNode()); #if EASTL_LIST_SIZE_CACHE ++mSize; #endif return static_cast(internalNode().mpPrev)->mValue; // Same as return back(); } template inline void* list::push_back_uninitialized() { node_type* const pNode = DoAllocateNode(); ((ListNodeBase*)pNode)->insert((ListNodeBase*)&internalNode()); #if EASTL_LIST_SIZE_CACHE ++mSize; #endif return &pNode->mValue; } template inline void list::pop_back() { #if EASTL_ASSERT_ENABLED if(EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) EASTL_FAIL_MSG("list::pop_back -- empty container"); #endif DoErase((ListNodeBase*)internalNode().mpPrev); } template template inline typename list::iterator list::emplace(const_iterator position, Args&&... args) { DoInsertValue(position.mpNode, eastl::forward(args)...); return iterator(position.mpNode->mpPrev); } template inline typename list::iterator list::insert(const_iterator position) { node_type* const pNode = DoCreateNode(value_type()); ((ListNodeBase*)pNode)->insert((ListNodeBase*)position.mpNode); #if EASTL_LIST_SIZE_CACHE ++mSize; #endif return (ListNodeBase*)pNode; } template inline typename list::iterator list::insert(const_iterator position, const value_type& value) { node_type* const pNode = DoCreateNode(value); ((ListNodeBase*)pNode)->insert((ListNodeBase*)position.mpNode); #if EASTL_LIST_SIZE_CACHE ++mSize; #endif return (ListNodeBase*)pNode; } template inline typename list::iterator list::insert(const_iterator position, value_type&& value) { return emplace(position, eastl::move(value)); } template inline typename list::iterator list::insert(const_iterator position, size_type n, const value_type& value) { iterator itPrev(position.mpNode); --itPrev; DoInsertValues((ListNodeBase*)position.mpNode, n, value); return ++itPrev; // Inserts in front of position, returns iterator to new elements. } template template inline typename list::iterator list::insert(const_iterator position, InputIterator first, InputIterator last) { iterator itPrev(position.mpNode); --itPrev; DoInsert((ListNodeBase*)position.mpNode, first, last, is_integral()); return ++itPrev; // Inserts in front of position, returns iterator to new elements. } template inline typename list::iterator list::insert(const_iterator position, std::initializer_list ilist) { iterator itPrev(position.mpNode); --itPrev; DoInsert((ListNodeBase*)position.mpNode, ilist.begin(), ilist.end(), false_type()); return ++itPrev; // Inserts in front of position, returns iterator to new elements. } template inline typename list::iterator list::erase(const_iterator position) { ++position; DoErase((ListNodeBase*)position.mpNode->mpPrev); return iterator(position.mpNode); } template typename list::iterator list::erase(const_iterator first, const_iterator last) { while(first != last) first = erase(first); return iterator(last.mpNode); } template inline typename list::reverse_iterator list::erase(const_reverse_iterator position) { return reverse_iterator(erase((++position).base())); } template typename list::reverse_iterator list::erase(const_reverse_iterator first, const_reverse_iterator last) { // Version which erases in order from first to last. // difference_type i(first.base() - last.base()); // while(i--) // first = erase(first); // return first; // Version which erases in order from last to first, but is slightly more efficient: const_iterator itLastBase((++last).base()); const_iterator itFirstBase((++first).base()); return reverse_iterator(erase(itLastBase, itFirstBase)); } template void list::remove(const value_type& value) { iterator current((ListNodeBase*)internalNode().mpNext); while(current.mpNode != &internalNode()) { if(EASTL_LIKELY(!(*current == value))) ++current; // We have duplicate '++current' statements here and below, but the logic here forces this. else { ++current; DoErase((ListNodeBase*)current.mpNode->mpPrev); } } } template template inline void list::remove_if(Predicate predicate) { for(iterator first((ListNodeBase*)internalNode().mpNext), last((ListNodeBase*)&internalNode()); first != last; ) { iterator temp(first); ++temp; if(predicate(first.mpNode->mValue)) DoErase((ListNodeBase*)first.mpNode); first = temp; } } template inline void list::reverse() EA_NOEXCEPT { ((ListNodeBase&)internalNode()).reverse(); } template inline void list::splice(const_iterator position, this_type& x) { // Splicing operations cannot succeed if the two containers use unequal allocators. // This issue is not addressed in the C++ 1998 standard but is discussed in the // LWG defect reports, such as #431. There is no simple solution to this problem. // One option is to throw an exception. Another option which probably captures the // user intent most of the time is to copy the range from the source to the dest and // remove it from the source. if(internalAllocator() == x.internalAllocator()) { #if EASTL_LIST_SIZE_CACHE if(x.mSize) { ((ListNodeBase*)position.mpNode)->splice((ListNodeBase*)x.internalNode().mpNext, (ListNodeBase*)&x.internalNode()); mSize += x.mSize; x.mSize = 0; } #else if(!x.empty()) ((ListNodeBase*)position.mpNode)->splice((ListNodeBase*)x.internalNode().mpNext, (ListNodeBase*)&x.internalNode()); #endif } else { insert(position, x.begin(), x.end()); x.clear(); } } template inline void list::splice(const_iterator position, this_type&& x) { return splice(position, x); // This will call splice(const_iterator, const this_type&); } template inline void list::splice(const_iterator position, list& x, const_iterator i) { if(internalAllocator() == x.internalAllocator()) { iterator i2(i.mpNode); ++i2; if((position != i) && (position != i2)) { ((ListNodeBase*)position.mpNode)->splice((ListNodeBase*)i.mpNode, (ListNodeBase*)i2.mpNode); #if EASTL_LIST_SIZE_CACHE ++mSize; --x.mSize; #endif } } else { insert(position, *i); x.erase(i); } } template inline void list::splice(const_iterator position, list&& x, const_iterator i) { return splice(position, x, i); // This will call splice(const_iterator, const this_type&, const_iterator); } template inline void list::splice(const_iterator position, this_type& x, const_iterator first, const_iterator last) { if(internalAllocator() == x.internalAllocator()) { #if EASTL_LIST_SIZE_CACHE const size_type n = (size_type)eastl::distance(first, last); if(n) { ((ListNodeBase*)position.mpNode)->splice((ListNodeBase*)first.mpNode, (ListNodeBase*)last.mpNode); mSize += n; x.mSize -= n; } #else if(first != last) ((ListNodeBase*)position.mpNode)->splice((ListNodeBase*)first.mpNode, (ListNodeBase*)last.mpNode); #endif } else { insert(position, first, last); x.erase(first, last); } } template inline void list::splice(const_iterator position, list&& x, const_iterator first, const_iterator last) { return splice(position, x, first, last); // This will call splice(const_iterator, const this_type&, const_iterator, const_iterator); } template inline void list::swap(this_type& x) { if(internalAllocator() == x.internalAllocator()) // If allocators are equivalent... DoSwap(x); else // else swap the contents. { const this_type temp(*this); // Can't call eastl::swap because that would *this = x; // itself call this member swap function. x = temp; } } template void list::merge(this_type& x) { if(this != &x) { iterator first(begin()); iterator firstX(x.begin()); const iterator last(end()); const iterator lastX(x.end()); while((first != last) && (firstX != lastX)) { if(*firstX < *first) { iterator next(firstX); splice(first, x, firstX, ++next); firstX = next; } else ++first; } if(firstX != lastX) splice(last, x, firstX, lastX); } } template void list::merge(this_type&& x) { return merge(x); // This will call merge(this_type&) } template template void list::merge(this_type& x, Compare compare) { if(this != &x) { iterator first(begin()); iterator firstX(x.begin()); const iterator last(end()); const iterator lastX(x.end()); while((first != last) && (firstX != lastX)) { if(compare(*firstX, *first)) { iterator next(firstX); splice(first, x, firstX, ++next); firstX = next; } else ++first; } if(firstX != lastX) splice(last, x, firstX, lastX); } } template template void list::merge(this_type&& x, Compare compare) { return merge(x, compare); // This will call merge(this_type&, Compare) } template void list::unique() { iterator first(begin()); const iterator last(end()); if(first != last) { iterator next(first); while(++next != last) { if(*first == *next) DoErase((ListNodeBase*)next.mpNode); else first = next; next = first; } } } template template void list::unique(BinaryPredicate predicate) { iterator first(begin()); const iterator last(end()); if(first != last) { iterator next(first); while(++next != last) { if(predicate(*first, *next)) DoErase((ListNodeBase*)next.mpNode); else first = next; next = first; } } } template void list::sort() { eastl::less compare; DoSort(begin(), end(), size(), compare); } template template void list::sort(Compare compare) { DoSort(begin(), end(), size(), compare); } template template typename list::iterator list::DoSort(iterator i1, iterator end2, size_type n, Compare& compare) { // A previous version of this function did this by creating temporary lists, // but that was incompatible with fixed_list because the sizes could be too big. // We sort subsegments by recursive descent. Then merge as we ascend. // Return an iterator to the beginning of the sorted subsegment. // Start with a special case for small node counts. switch (n) { case 0: case 1: return i1; case 2: // Potentialy swap these two nodes and return the resulting first of them. if(compare(*--end2, *i1)) { end2.mpNode->remove(); end2.mpNode->insert(i1.mpNode); return end2; } return i1; case 3: { // We do a list insertion sort. Measurements showed this improved performance 3-12%. iterator lowest = i1; for(iterator current = i1.next(); current != end2; ++current) { if(compare(*current, *lowest)) lowest = current; } if(lowest == i1) ++i1; else { lowest.mpNode->remove(); lowest.mpNode->insert(i1.mpNode); } if(compare(*--end2, *i1)) // At this point, i1 refers to the second element in this three element segment. { end2.mpNode->remove(); end2.mpNode->insert(i1.mpNode); } return lowest; } } // Divide the range into two parts are recursively sort each part. Upon return we will have // two halves that are each sorted but we'll need to merge the two together before returning. iterator result; size_type nMid = (n / 2); iterator end1 = eastl::next(i1, (difference_type)nMid); i1 = DoSort(i1, end1, nMid, compare); // Return the new beginning of the first sorted sub-range. iterator i2 = DoSort(end1, end2, n - nMid, compare); // Return the new beginning of the second sorted sub-range. // If the start of the second list is before the start of the first list, insert the first list // into the second at an appropriate starting place. if(compare(*i2, *i1)) { // Find the position to insert the first list into the second list. iterator ix = i2.next(); while((ix != end2) && compare(*ix, *i1)) ++ix; // Cut out the initial segment of the second list and move it to be in front of the first list. ListNodeBase* i2Cut = i2.mpNode; ListNodeBase* i2CutLast = ix.mpNode->mpPrev; result = i2; end1 = i2 = ix; ListNodeBase::remove_range(i2Cut, i2CutLast); i1.mpNode->insert_range(i2Cut, i2CutLast); } else { result = i1; end1 = i2; } // Merge the two segments. We do this by merging the second sub-segment into the first, by walking forward in each of the two sub-segments. for(++i1; (i1 != end1) && (i2 != end2); ++i1) // while still working on either segment... { if(compare(*i2, *i1)) // If i2 is less than i1 and it needs to be merged in front of i1... { // Find the position to insert the i2 list into the i1 list. iterator ix = i2.next(); while((ix != end2) && compare(*ix, *i1)) ++ix; // Cut this section of the i2 sub-segment out and merge into the appropriate place in the i1 list. ListNodeBase* i2Cut = i2.mpNode; ListNodeBase* i2CutLast = ix.mpNode->mpPrev; if(end1 == i2) end1 = ix; i2 = ix; ListNodeBase::remove_range(i2Cut, i2CutLast); i1.mpNode->insert_range(i2Cut, i2CutLast); } } return result; } template template inline typename list::node_type* list::DoCreateNode(Args&&... args) { node_type* const pNode = DoAllocateNode(); // pNode is of type node_type, but it's uninitialized memory. #if EASTL_EXCEPTIONS_ENABLED try { ::new((void*)&pNode->mValue) value_type(eastl::forward(args)...); } catch(...) { DoFreeNode(pNode); throw; } #else ::new((void*)&pNode->mValue) value_type(eastl::forward(args)...); #endif return pNode; } template inline typename list::node_type* list::DoCreateNode() { node_type* const pNode = DoAllocateNode(); #if EASTL_EXCEPTIONS_ENABLED try { ::new((void*)&pNode->mValue) value_type(); } catch(...) { DoFreeNode(pNode); throw; } #else ::new((void*)&pNode->mValue) value_type; #endif return pNode; } template template inline void list::DoAssign(Integer n, Integer value, true_type) { DoAssignValues(static_cast(n), static_cast(value)); } template template void list::DoAssign(InputIterator first, InputIterator last, false_type) { node_type* pNode = static_cast(internalNode().mpNext); for(; (pNode != &internalNode()) && (first != last); ++first) { pNode->mValue = *first; pNode = static_cast(pNode->mpNext); } if(first == last) erase(const_iterator((ListNodeBase*)pNode), (ListNodeBase*)&internalNode()); else DoInsert((ListNodeBase*)&internalNode(), first, last, false_type()); } template void list::DoAssignValues(size_type n, const value_type& value) { node_type* pNode = static_cast(internalNode().mpNext); for(; (pNode != &internalNode()) && (n > 0); --n) { pNode->mValue = value; pNode = static_cast(pNode->mpNext); } if(n) DoInsertValues((ListNodeBase*)&internalNode(), n, value); else erase(const_iterator((ListNodeBase*)pNode), (ListNodeBase*)&internalNode()); } template template inline void list::DoInsert(ListNodeBase* pNode, Integer n, Integer value, true_type) { DoInsertValues(pNode, static_cast(n), static_cast(value)); } template template inline void list::DoInsert(ListNodeBase* pNode, InputIterator first, InputIterator last, false_type) { for(; first != last; ++first) DoInsertValue(pNode, *first); } template inline void list::DoInsertValues(ListNodeBase* pNode, size_type n, const value_type& value) { for(; n > 0; --n) DoInsertValue(pNode, value); } template template inline void list::DoInsertValue(ListNodeBase* pNode, Args&&... args) { node_type* const pNodeNew = DoCreateNode(eastl::forward(args)...); ((ListNodeBase*)pNodeNew)->insert(pNode); #if EASTL_LIST_SIZE_CACHE ++mSize; #endif } template inline void list::DoErase(ListNodeBase* pNode) { pNode->remove(); ((node_type*)pNode)->~node_type(); DoFreeNode(((node_type*)pNode)); #if EASTL_LIST_SIZE_CACHE --mSize; #endif /* Test version that uses union intermediates union { ListNodeBase* mpBase; node_type* mpNode; } node = { pNode }; node.mpNode->~node_type(); node.mpBase->remove(); DoFreeNode(node.mpNode); #if EASTL_LIST_SIZE_CACHE --mSize; #endif */ } template inline void list::DoSwap(this_type& x) { ListNodeBase::swap((ListNodeBase&)internalNode(), (ListNodeBase&)x.internalNode()); // We need to implement a special swap because we can't do a shallow swap. eastl::swap(internalAllocator(), x.internalAllocator()); // We do this even if EASTL_ALLOCATOR_COPY_ENABLED is 0. #if EASTL_LIST_SIZE_CACHE eastl::swap(mSize, x.mSize); #endif } template inline bool list::validate() const { #if EASTL_LIST_SIZE_CACHE size_type n = 0; for(const_iterator i(begin()), iEnd(end()); i != iEnd; ++i) ++n; if(n != mSize) return false; #endif // To do: More validation. return true; } template inline int list::validate_iterator(const_iterator i) const { // To do: Come up with a more efficient mechanism of doing this. for(const_iterator temp = begin(), tempEnd = end(); temp != tempEnd; ++temp) { if(temp == i) return (isf_valid | isf_current | isf_can_dereference); } if(i == end()) return (isf_valid | isf_current); return isf_none; } /////////////////////////////////////////////////////////////////////// // global operators /////////////////////////////////////////////////////////////////////// template bool operator==(const list& a, const list& b) { typename list::const_iterator ia = a.begin(); typename list::const_iterator ib = b.begin(); typename list::const_iterator enda = a.end(); #if EASTL_LIST_SIZE_CACHE if(a.size() == b.size()) { while((ia != enda) && (*ia == *ib)) { ++ia; ++ib; } return (ia == enda); } return false; #else typename list::const_iterator endb = b.end(); while((ia != enda) && (ib != endb) && (*ia == *ib)) { ++ia; ++ib; } return (ia == enda) && (ib == endb); #endif } template bool operator<(const list& a, const list& b) { return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); } template bool operator!=(const list& a, const list& b) { return !(a == b); } template bool operator>(const list& a, const list& b) { return b < a; } template bool operator<=(const list& a, const list& b) { return !(b < a); } template bool operator>=(const list& a, const list& b) { return !(a < b); } template void swap(list& a, list& b) { a.swap(b); } /////////////////////////////////////////////////////////////////////// // erase / erase_if // // https://en.cppreference.com/w/cpp/container/list/erase2 /////////////////////////////////////////////////////////////////////// template void erase(list& c, const U& value) { // Erases all elements that compare equal to value from the container. c.remove_if([&](auto& elem) { return elem == value; }); } template void erase_if(list& c, Predicate predicate) { // Erases all elements that satisfy the predicate pred from the container. c.remove_if(predicate); } } // namespace eastl EA_RESTORE_SN_WARNING() EA_RESTORE_VC_WARNING(); #endif // Header include guard