Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
DoublyLinkedList.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
5
7
8// Would like to leave on by default but we just added and cannot afford to have debug builds get that slow
9#ifndef qStroika_Foundation_Containers_DataStructures_DoublyLinkedList_IncludeSlowDebugChecks_
10#define qStroika_Foundation_Containers_DataStructures_DoublyLinkedList_IncludeSlowDebugChecks_ 0
11#endif
12
13 /*
14 ********************************************************************************
15 ***************************** DoublyLinkedList<T>::Link_ ***********************
16 ********************************************************************************
17 */
18 template <typename T>
19 constexpr DoublyLinkedList<T>::Link_::Link_ (ArgByValueType<T> item, Link_* prev, Link_* next)
20 : fItem{item}
21 , fPrev{prev}
22 , fNext{next}
23 {
24 }
25
26 /*
27 ********************************************************************************
28 ******************************* DoublyLinkedList<T> ****************************
29 ********************************************************************************
30 */
31 template <typename T>
32 inline DoublyLinkedList<T>::DoublyLinkedList ()
33 {
34 Invariant ();
35 }
36 template <typename T>
37 DoublyLinkedList<T>::DoublyLinkedList (const DoublyLinkedList& src)
38 {
39 for (const Link_* cur = src.fHead_; cur != nullptr; cur = cur->fNext) {
40 push_back (cur->fItem);
41 }
42 Invariant ();
43 }
44 template <typename T>
45 DoublyLinkedList<T>::DoublyLinkedList (DoublyLinkedList&& src)
46 : fHead_{src.fHead_}
47 , fTail_{src.fTail_}
48 {
49 Invariant ();
50 src.fHead_ = nullptr;
51 src.fTail_ = nullptr;
52 src.Invariant ();
53 }
54 template <typename T>
55 inline DoublyLinkedList<T>::~DoublyLinkedList ()
56 {
57 /*
58 * This could be a little cheaper - we could avoid setting fHead_ pointer,
59 * but we must worry more about codeSize/re-use.
60 * That would involve a new function that COULD NOT BE INLINED.
61 *
62 * < I guess I could add a hack method - unadvertised - but has to be
63 * at least protected - and call it here to do what I've mentioned above >
64 */
65 Invariant ();
66 clear ();
67 Invariant ();
68 Ensure (size () == 0);
69 Ensure (fHead_ == nullptr);
70 Ensure (fTail_ == nullptr);
71 }
72 template <typename T>
73 inline void DoublyLinkedList<T>::Invariant () const noexcept
74 {
75#if qStroika_Foundation_Debug_AssertionsChecked
76 Invariant_ ();
77#endif
78 }
79 template <typename T>
80 inline bool DoublyLinkedList<T>::empty () const
81 {
82 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
83 return fHead_ == nullptr;
84 }
85 template <typename T>
86 inline size_t DoublyLinkedList<T>::size () const
87 {
88 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
89 size_t n = 0;
90 for (const Link_* i = fHead_; i != nullptr; i = i->fNext) {
91 ++n;
92 }
93 return n;
94 }
95 template <typename T>
96 inline optional<T> DoublyLinkedList<T>::GetFirst () const
97 {
98 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
99 return fHead_ == nullptr ? optional<T>{} : fHead_->fItem;
100 }
101 template <typename T>
102 inline optional<T> DoublyLinkedList<T>::GetLast () const
103 {
104 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
105 return fTail_ == nullptr ? optional<T>{} : fTail_->fItem;
106 }
107 template <typename T>
108 inline void DoublyLinkedList<T>::push_front (ArgByValueType<T> item)
111 Invariant ();
112 fHead_ = new Link_{item, nullptr, fHead_};
113 if (fHead_->fNext != nullptr) [[likely]] {
114 // backlink second item to first
115 fHead_->fNext->fPrev = fHead_;
116 }
117 if (fTail_ == nullptr) [[unlikely]] {
118 // if last is null, list was empty, so first==last now
119 fTail_ = fHead_;
120 }
121 Invariant ();
122 }
123 template <typename T>
124 template <Memory::ISpanOfT<T> SPAN_T>
125 void DoublyLinkedList<T>::push_front (const SPAN_T& copyFrom)
126 {
127 // push_front in reverse order cuz push_front reverses traversal order, and two wrongs make a right
128 for (auto ri = copyFrom.rbegin (); ri != copyFrom.rend (); ++ri) {
129 push_front (*ri);
130 }
131 }
132 template <typename T>
133 inline void DoublyLinkedList<T>::push_back (ArgByValueType<T> item)
136 Invariant ();
137 fTail_ = new Link_{item, fTail_, nullptr};
138 if (fTail_->fPrev != nullptr) [[likely]] {
139 // forward link second to last item to its prev
140 fTail_->fPrev->fNext = fTail_;
141 }
142 if (fHead_ == nullptr) [[unlikely]] {
143 // if head is null, list was empty, so first==last now
144 fHead_ = fTail_;
145 }
146 Invariant ();
147 }
148 template <typename T>
149 template <Memory::ISpanOfT<T> SPAN_T>
150 void DoublyLinkedList<T>::push_back (const SPAN_T& copyFrom)
151 {
152 for (auto i : copyFrom) {
153 push_back (i);
154 }
155 }
156 template <typename T>
158 {
160 RequireNotNull (fHead_);
161 Invariant ();
162 Link_* victim = fHead_;
163 Assert (victim->fPrev == nullptr); // cuz it was first..
164 /*
165 * Before:
166 * | | | | | |
167 * | V | | B | | C |
168 * | <-prev | | <-prev | | <-prev | ...
169 * | next-> | | next-> | | next-> |
170 * | | | | | |
171 *
172 * After:
173 * | | | |
174 * | B | | C |
175 * | <-prev | | <-prev | ...
176 * | next-> | | next-> |
177 * | | | |
178 */
179 fHead_ = victim->fNext; // First points to B
180 if (fHead_ == nullptr) {
181 Assert (victim == fTail_);
182 fTail_ = nullptr;
183 }
184 else {
185 Assert (fHead_->fPrev == victim);
186 fHead_->fPrev = nullptr; // B's prev is Nil since it is new first
187 }
188 delete victim;
189 Invariant ();
190 }
191 template <typename T>
193 {
195 RequireNotNull (fHead_);
196 Invariant ();
197 Link_* victim = fTail_;
198 Assert (victim->fNext == nullptr); // cuz it was last..
199 /*
200 * Before:
201 * | | | | | |
202 * | A | | B | | V |
203 * ... | <-prev | | <-prev | | <-prev |
204 * | next-> | | next-> | | next-> |
205 * | | | | | |
207 * After:
208 * | | | |
209 * | A | | B |
210 * ... | <-prev | | <-prev |
211 * | next-> | | next-> |
212 * | | | |
213 */
214 fTail_ = victim->fPrev; // new last item
215 if (fTail_ == nullptr) {
216 Assert (fHead_ == victim);
217 fHead_ = nullptr;
218 }
219 else {
220 Assert (fTail_->fNext == victim);
221 fTail_->fNext = nullptr; // B's fNext is Nil since it is new last
223 delete victim;
224 Invariant ();
225 }
226 template <typename T>
227 auto DoublyLinkedList<T>::operator= (const DoublyLinkedList& rhs) -> DoublyLinkedList&
228 {
230 Invariant ();
231 clear ();
232 /*
233 * Copy the linked list by keeping a point to the new current and new
234 * previous, and sliding them along in parallel as we construct the
235 * new list. Only do this if we have at least one element - then we
236 * don't have to worry about the head of the list, or nullptr ptrs, etc - that
237 * case is handled outside, before the loop.
238 */
239 if (rhs.fHead_ != nullptr) {
240 fHead_ = new Link_{rhs.fHead_->fItem, nullptr};
241 Link_* newCur = fHead_;
242 for (const Link_* cur = rhs.fHead_->fNext; cur != nullptr; cur = cur->fNext) {
243 Link_* newPrev = newCur;
244 newCur = new Link_{cur->fItem, nullptr};
245 newPrev->fNext = newCur;
246 }
247 }
248 Invariant ();
249 return *this;
251 template <typename T>
252 template <typename EQUALS_COMPARER>
253 void DoublyLinkedList<T>::Remove (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer)
254 {
256 Invariant ();
257 if (equalsComparer (item, fHead_->fItem)) {
258 RemoveFirst ();
260 else {
261 Link_* prev = nullptr;
262 for (Link_* link = fHead_; link != nullptr; prev = link, link = link->fNext) {
263 if (equalsComparer (link->fItem, item)) {
264 AssertNotNull (prev); // cuz otherwise we would have hit it in first case!
265 prev->fNext = link->fNext;
266 delete (link);
267 break;
268 }
269 }
270 }
271 Invariant ();
272 }
273 template <typename T>
274 template <typename EQUALS_COMPARER>
275 bool DoublyLinkedList<T>::Contains (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer) const
276 {
277 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
278 for (const Link_* current = fHead_; current != nullptr; current = current->fNext) {
279 if (forward<EQUALS_COMPARER> (equalsComparer) (current->fItem, item)) {
280 return true;
281 }
282 }
283 return false;
284 }
285 template <typename T>
286 template <invocable<T> FUNCTION>
287 inline void DoublyLinkedList<T>::Apply (FUNCTION&& doToElement) const
288 {
289 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
290 for (const Link_* i = fHead_; i != nullptr; i = i->fNext) {
291 forward<FUNCTION> (doToElement) (i->fItem);
292 }
293 }
294 template <typename T>
295 template <typename FUNCTION>
296 inline auto DoublyLinkedList<T>::Find (FUNCTION&& firstThat) const -> UnderlyingIteratorRep
297 {
298 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
299 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
300 if (forward<FUNCTION> (firstThat) (i->fItem)) {
301 return i;
302 }
303 }
304 return nullptr;
305 }
306 template <typename T>
308 {
310 for (Link_* i = fHead_; i != nullptr;) {
311 Link_* deleteMe = i;
312 i = i->fNext;
313 delete deleteMe;
314 }
315 fHead_ = nullptr;
316 fTail_ = nullptr;
317 }
318 template <typename T>
319 T DoublyLinkedList<T>::GetAt (size_t i) const
320 {
321 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
322 Require (i >= 0);
323 Require (i < size ());
324 const Link_* cur = fHead_;
325 for (; i != 0; cur = cur->fNext, --i) {
326 AssertNotNull (cur); // cuz i <= fLength
327 }
328 AssertNotNull (cur); // cuz i <= fLength
329 return (cur->fItem);
330 }
331 template <typename T>
332 void DoublyLinkedList<T>::SetAt (size_t i, ArgByValueType<T> item)
333 {
335 Require (i >= 0);
336 Require (i < size ());
337 Link_* cur = fHead_;
338 for (; i != 0; cur = cur->fNext, --i) {
339 AssertNotNull (cur); // cuz i <= fLength
340 }
341 AssertNotNull (cur); // cuz i <= fLength
342 cur->fItem = item;
343 }
344 template <typename T>
345 inline void DoublyLinkedList<T>::MoveIteratorHereAfterClone (ForwardIterator* pi, const DoublyLinkedList<T>* movedFrom) const
346 {
347 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
348 // TRICKY TODO - BUT MUST DO - MUST MOVE FROM OLD ITER TO NEW
349 // only way
350 //
351 // For STL containers, not sure how to find an equiv new iterator for an old one, but my best guess is to iterate through
352 // old for old, and when I match, stop on new
353#if qStroika_Foundation_Debug_AssertionsChecked
354 Require (pi->fData_ == movedFrom);
355#endif
356 auto newI = this->fHead_;
357 [[maybe_unused]] auto newE = nullptr;
358 auto oldI = movedFrom->fHead_;
359 [[maybe_unused]] auto oldE = nullptr;
360 while (oldI != pi->fCurrent_) {
361 Assert (newI != newE);
362 Assert (oldI != oldE);
363 newI = newI->fNext;
364 oldI = oldI->fNext;
365 Assert (newI != newE);
366 Assert (oldI != oldE);
367 }
368 Assert (oldI == pi->fCurrent_);
369 pi->fCurrent_ = newI;
370#if qStroika_Foundation_Debug_AssertionsChecked
371 pi->fData_ = this;
372#endif
373 }
374 template <typename T>
375 inline auto DoublyLinkedList<T>::begin () const -> ForwardIterator
376 {
377 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
378 return ForwardIterator{this};
379 }
380 template <typename T>
381 constexpr auto DoublyLinkedList<T>::end () const noexcept -> ForwardIterator
382 {
383 return ForwardIterator{};
384 }
385 template <typename T>
386 auto DoublyLinkedList<T>::erase (const ForwardIterator& i) -> ForwardIterator
387 {
388 ForwardIterator next = i;
389 ++next;
390 Remove (i);
391 next.Invariant ();
392 return next;
393 }
394 template <typename T>
395 void DoublyLinkedList<T>::Remove (const ForwardIterator& i)
396 {
398 Require (not i.Done ());
399#if qStroika_Foundation_Debug_AssertionsChecked
400 Require (i.fData_ == this); // assure iterator not stale
401#endif
402 this->Invariant ();
403
404 const Link_* victim = i.fCurrent_;
405 AssertNotNull (victim); // cuz not done
406 /*
407 * Before:
408 * | | | | | |
409 * | A | | V | | C |
410 * ... | <-prev | | <-prev | | <-prev | ...
411 * | next-> | | next-> | | next-> |
412 * | | | | | |
413 *
414 * After:
415 * | | | |
416 * | A | | C |
417 * ... | <-prev | | <-prev | ...
418 * | next-> | | next-> |
419 * | | | |
420 */
421 if (victim->fPrev == nullptr) {
422 // In this case 'A' does not exist - it is Nil...
423 Assert (fHead_ == victim);
424 fHead_ = victim->fNext; // 'C' is now first
425 if (fHead_ == nullptr) {
426 fTail_ = nullptr;
427 }
428 else {
429 Assert (fHead_->fPrev == victim); // Victim used to be 'C's prev
430 fHead_->fPrev = nullptr; // Now Nil!
431 }
432 }
433 else {
434 Assert (victim->fPrev->fNext == victim); // In this case 'A' DOES exist
435 victim->fPrev->fNext = victim->fNext; // Make A point to C
436 // Now make 'C' point back to A (careful if 'C' is Nil)
437 if (victim->fNext == nullptr) {
438 // In this case 'C' does not exist - it is Nil...
439 Assert (victim == fTail_);
440 fTail_ = victim->fPrev; // 'A' is now last
441 }
442 else {
443 Assert (victim->fNext->fPrev == victim); // Victim used to be 'C's prev
444 victim->fNext->fPrev = victim->fPrev; // Now 'A' is
445 }
446 }
447 delete victim;
448 this->Invariant ();
449 }
450 template <typename T>
451 inline void DoublyLinkedList<T>::SetAt (const ForwardIterator& i, ArgByValueType<T> newValue)
452 {
454 Require (not i.Done ());
455#if qStroika_Foundation_Debug_AssertionsChecked
456 Require (i.fData_ == this); // assure iterator not stale
457#endif
458 this->Invariant ();
459 const_cast<Link_*> (i.fCurrent_)->fItem = newValue;
460 this->Invariant ();
461 }
462 template <typename T>
463 void DoublyLinkedList<T>::AddBefore (const ForwardIterator& i, ArgByValueType<T> newValue)
464 {
466#if qStroika_Foundation_Debug_AssertionsChecked
467 Require (i.fData_ == this); // assure iterator not stale
468#endif
469 /*
470 * NB: This code works fine, even if we are done!!!
471 */
472 this->Invariant ();
473 if (i.fCurrent_ == nullptr) {
474 /*
475 * NB: If I am past the last item on the list, AddBefore() is equivalent
476 * to Appending to the list.
477 */
478 Assert (i.Done ());
479 push_back (newValue);
480 Assert (i.Done ()); // what is done, cannot be undone!!!
481 }
482 else {
483 Link_* prev = i.fCurrent_->fPrev;
484 if (prev == nullptr) {
485 push_front (newValue);
486 }
487 else {
488 /*
489 * | | | | | |
490 * | PREV | | NEW | | CUR |
491 * ... | <-prev | | <-prev | | <-prev | ...
492 * | next-> | | next-> | | next-> |
493 * | | | | | |
494 */
495 Assert (prev->fNext == i.fCurrent_);
496 Link_* iteratorCurLink = const_cast<Link_*> (i.fCurrent_);
497 prev->fNext = new Link_{newValue, prev, iteratorCurLink};
498 // Since fCurrent != nullptr from above, we update its prev, and don't have
499 // to worry about fTail_.
500 iteratorCurLink->fPrev = prev->fNext;
501 Assert (i.fCurrent_->fPrev->fPrev == prev); // old prev is two back now...
502 }
503 }
504 this->Invariant ();
505 }
506 template <typename T>
507 inline void DoublyLinkedList<T>::AddAfter (const ForwardIterator& i, ArgByValueType<T> newValue)
508 {
510#if qStroika_Foundation_Debug_AssertionsChecked
511 Require (i.fData_ == this); // assure iterator not stale
512#endif
513 this->Invariant ();
514 Require (not i.Done ());
515 AssertNotNull (i.fCurrent_); // since not done...
516 Assert (fHead_ != nullptr);
517 /*
518 * | | | |
519 * | CUR | | NEW |
520 * ... | <-prev | | <-prev | ...
521 * | next-> | | next-> |
522 * | | | |
523 */
524 Link_* iteratorCurLink = const_cast<Link_*> (i.fCurrent_);
525 Link_* newLink = new Link_{newValue, iteratorCurLink, iteratorCurLink->fNext};
526 iteratorCurLink->fNext = newLink;
527 if (newLink->fNext != nullptr) {
528 newLink->fNext->fPrev = newLink;
529 }
530 if (newLink->fNext == nullptr) {
531 fTail_ = newLink;
532 }
533 else {
534 Assert (newLink->fNext->fPrev == newLink); // cuz of params to new Link_...
535 }
536 this->Invariant ();
537 }
538#if qStroika_Foundation_Debug_AssertionsChecked
539 template <typename T>
540 void DoublyLinkedList<T>::Invariant_ () const noexcept
541 {
542#if qStroika_Foundation_Containers_DataStructures_DoublyLinkedList_IncludeSlowDebugChecks_
543 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
544#endif
545 if (fHead_ != nullptr) {
546 Assert (fHead_->fPrev == nullptr);
547 if (fHead_->fNext == nullptr) {
548 Assert (fHead_ == fTail_);
549 }
550 else {
551 Assert (fHead_->fNext->fPrev == fHead_);
552 }
553 }
554 if (fTail_ != nullptr) {
555 Assert (fTail_->fNext == nullptr);
556 if (fTail_->fPrev == nullptr) {
557 Assert (fHead_ == fTail_);
558 }
559 else {
560 Assert (fTail_->fPrev->fNext == fTail_);
561 }
562 }
563 Assert (fHead_ == nullptr or fHead_->fPrev == nullptr);
564 Assert (fTail_ == nullptr or fTail_->fNext == nullptr);
565
566 /*
567 * Check we are properly linked together.
568 */
569 size_t forwardCounter = 0;
570 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
571 Assert (i->fNext == nullptr or i->fNext->fPrev == i); // adjacent nodes point at each other
572 ++forwardCounter;
573 }
574 size_t backwardCounter{};
575 for (Link_* i = fTail_; i != nullptr; i = i->fPrev) {
576 Assert (i->fPrev == nullptr or i->fPrev->fNext == i); // adjacent nodes point at each other
577 ++backwardCounter;
578 }
579 Assert (forwardCounter == backwardCounter);
580 }
581#endif
582
583 /*
584 ********************************************************************************
585 ********************** DoublyLinkedList<T>::ForwardIterator ********************
586 ********************************************************************************
587 */
588 template <typename T>
589 constexpr DoublyLinkedList<T>::ForwardIterator::ForwardIterator ([[maybe_unused]] const DoublyLinkedList* data, UnderlyingIteratorRep startAt) noexcept
590 : fCurrent_{startAt}
591#if qStroika_Foundation_Debug_AssertionsChecked
592 , fData_{data}
593#endif
594 {
595 }
596 template <typename T>
597 constexpr DoublyLinkedList<T>::ForwardIterator::ForwardIterator (const DoublyLinkedList* data) noexcept
598 : ForwardIterator{data, (RequireExpression (data != nullptr), data->fHead_)}
599 {
600 }
601 template <typename T>
602 inline void DoublyLinkedList<T>::ForwardIterator::Invariant () const noexcept
603 {
604#if qStroika_Foundation_Debug_AssertionsChecked
605 Invariant_ ();
606#endif
607 }
608 template <typename T>
609 inline DoublyLinkedList<T>::ForwardIterator::operator bool () const
610 {
611 return not Done ();
612 }
613 template <typename T>
614 inline bool DoublyLinkedList<T>::ForwardIterator::Done () const noexcept
615 {
616#if qStroika_Foundation_Debug_AssertionsChecked
617 if (fData_ != nullptr) {
618 AssertExternallySynchronizedMutex::ReadContext declareContext{*fData_};
619 Invariant ();
620 }
621#endif
622 return fCurrent_ == nullptr;
623 }
624 template <typename T>
625 inline auto DoublyLinkedList<T>::ForwardIterator::operator++ () noexcept -> ForwardIterator&
626 {
627 Require (not Done ());
628 Invariant ();
629 Assert (fCurrent_ != nullptr);
630 fCurrent_ = fCurrent_->fNext;
631 Invariant ();
632 return *this;
633 }
634 template <typename T>
635 inline auto DoublyLinkedList<T>::ForwardIterator::operator++ (int) noexcept -> ForwardIterator
636 {
637 ForwardIterator result = *this;
638 this->operator++ ();
639 return result;
640 }
641 template <typename T>
642 inline const T& DoublyLinkedList<T>::ForwardIterator::operator* () const
643 {
644#if qStroika_Foundation_Debug_AssertionsChecked
645 RequireNotNull (fData_);
646 AssertExternallySynchronizedMutex::ReadContext declareContext{*fData_};
647#endif
648 Require (not Done ());
649 Invariant ();
650 AssertNotNull (fCurrent_);
651 return fCurrent_->fItem;
652 }
653 template <typename T>
654 inline const T* DoublyLinkedList<T>::ForwardIterator::operator->() const
655 {
656#if qStroika_Foundation_Debug_AssertionsChecked
657 RequireNotNull (fData_);
658 AssertExternallySynchronizedMutex::ReadContext declareContext{*fData_};
659#endif
660 Require (not Done ());
661 Invariant ();
662 AssertNotNull (fCurrent_);
663 return &fCurrent_->fItem;
664 }
665 template <typename T>
666 size_t DoublyLinkedList<T>::ForwardIterator::CurrentIndex (const DoublyLinkedList* data) const
667 {
668 Require (not Done ());
669#if qStroika_Foundation_Debug_AssertionsChecked
670 Require (data == fData_);
671 RequireNotNull (fData_);
672#endif
673 RequireNotNull (this->fCurrent_);
674 size_t i = 0;
675 for (const Link_* l = data->fHead_;; l = l->fNext, ++i) {
676 AssertNotNull (l);
677 if (l == fCurrent_) [[unlikely]] {
678 return i;
679 }
680 }
682 return i;
683 }
684 template <typename T>
685 inline auto DoublyLinkedList<T>::ForwardIterator::GetUnderlyingIteratorRep () const -> UnderlyingIteratorRep
686 {
687 return fCurrent_;
688 }
689 template <typename T>
690 inline void DoublyLinkedList<T>::ForwardIterator::SetUnderlyingIteratorRep (UnderlyingIteratorRep l)
691 {
692#if qStroika_Foundation_Debug_AssertionsChecked
693 AssertExternallySynchronizedMutex::ReadContext declareContext{*fData_}; // read lock on data, though writing to this iterator
694#endif
695 // MUST COME FROM THIS LIST
696 // CAN be nullptr
697 fCurrent_ = l;
698 }
699 template <typename T>
700 constexpr void DoublyLinkedList<T>::ForwardIterator::AssertDataMatches ([[maybe_unused]] const DoublyLinkedList* data) const
701 {
702#if qStroika_Foundation_Debug_AssertionsChecked
703 Require (data == fData_);
704#endif
705 }
706 template <typename T>
707 inline bool DoublyLinkedList<T>::ForwardIterator::operator== (const ForwardIterator& rhs) const
708 {
709 return fCurrent_ == rhs.fCurrent_;
710 }
711#if qStroika_Foundation_Debug_AssertionsChecked
712 template <typename T>
713 void DoublyLinkedList<T>::ForwardIterator::Invariant_ () const noexcept
714 {
715 }
716#endif
717
718}
#define AssertNotNull(p)
Definition Assertions.h:333
#define RequireNotNull(p)
Definition Assertions.h:347
#define RequireExpression(c)
Definition Assertions.h:267
#define AssertNotReached()
Definition Assertions.h:355
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...