Stroika Library 3.0d16
 
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 RemoveAll ();
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;
107 template <typename T>
108 inline void DoublyLinkedList<T>::push_front (ArgByValueType<T> item)
109 {
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 inline void DoublyLinkedList<T>::push_back (ArgByValueType<T> item)
125 {
127 Invariant ();
128 fTail_ = new Link_{item, fTail_, nullptr};
129 if (fTail_->fPrev != nullptr) [[likely]] {
130 // forward link second to last item to its prev
131 fTail_->fPrev->fNext = fTail_;
132 }
133 if (fHead_ == nullptr) [[unlikely]] {
134 // if head is null, list was empty, so first==last now
135 fHead_ = fTail_;
136 }
137 Invariant ();
138 }
139 template <typename T>
141 {
143 RequireNotNull (fHead_);
144 Invariant ();
145 Link_* victim = fHead_;
146 Assert (victim->fPrev == nullptr); // cuz it was first..
147 /*
148 * Before:
149 * | | | | | |
150 * | V | | B | | C |
151 * | <-prev | | <-prev | | <-prev | ...
152 * | next-> | | next-> | | next-> |
153 * | | | | | |
154 *
155 * After:
156 * | | | |
157 * | B | | C |
158 * | <-prev | | <-prev | ...
159 * | next-> | | next-> |
160 * | | | |
161 */
162 fHead_ = victim->fNext; // First points to B
163 if (fHead_ == nullptr) {
164 Assert (victim == fTail_);
165 fTail_ = nullptr;
166 }
167 else {
168 Assert (fHead_->fPrev == victim);
169 fHead_->fPrev = nullptr; // B's prev is Nil since it is new first
170 }
171 delete victim;
172 Invariant ();
173 }
174 template <typename T>
176 {
178 RequireNotNull (fHead_);
179 Invariant ();
180 Link_* victim = fTail_;
181 Assert (victim->fNext == nullptr); // cuz it was last..
182 /*
183 * Before:
184 * | | | | | |
185 * | A | | B | | V |
186 * ... | <-prev | | <-prev | | <-prev |
187 * | next-> | | next-> | | next-> |
188 * | | | | | |
189 *
190 * After:
191 * | | | |
192 * | A | | B |
193 * ... | <-prev | | <-prev |
194 * | next-> | | next-> |
195 * | | | |
196 */
197 fTail_ = victim->fPrev; // new last item
198 if (fTail_ == nullptr) {
199 Assert (fHead_ == victim);
200 fHead_ = nullptr;
201 }
202 else {
203 Assert (fTail_->fNext == victim);
204 fTail_->fNext = nullptr; // B's fNext is Nil since it is new last
205 }
206 delete victim;
207 Invariant ();
208 }
209 template <typename T>
210 auto DoublyLinkedList<T>::operator= (const DoublyLinkedList& rhs) -> DoublyLinkedList&
211 {
213 Invariant ();
214 RemoveAll ();
215 /*
216 * Copy the linked list by keeping a point to the new current and new
217 * previous, and sliding them along in parallel as we construct the
218 * new list. Only do this if we have at least one element - then we
219 * don't have to worry about the head of the list, or nullptr ptrs, etc - that
220 * case is handled outside, before the loop.
221 */
222 if (rhs.fHead_ != nullptr) {
223 fHead_ = new Link_{rhs.fHead_->fItem, nullptr};
224 Link_* newCur = fHead_;
225 for (const Link_* cur = rhs.fHead_->fNext; cur != nullptr; cur = cur->fNext) {
226 Link_* newPrev = newCur;
227 newCur = new Link_{cur->fItem, nullptr};
228 newPrev->fNext = newCur;
229 }
230 }
231 Invariant ();
232 return *this;
233 }
234 template <typename T>
235 template <typename EQUALS_COMPARER>
236 void DoublyLinkedList<T>::Remove (ArgByValueType<T> item, EQUALS_COMPARER&& equalsComparer)
237 {
238 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
239 Invariant ();
240 if (equalsComparer (item, fHead_->fItem)) {
241 RemoveFirst ();
242 }
243 else {
244 Link_* prev = nullptr;
245 for (Link_* link = fHead_; link != nullptr; prev = link, link = link->fNext) {
246 if (equalsComparer (link->fItem, item)) {
247 AssertNotNull (prev); // cuz otherwise we would have hit it in first case!
248 prev->fNext = link->fNext;
249 delete (link);
250 break;
251 }
252 }
253 }
254 Invariant ();
255 }
256 template <typename T>
257 template <typename EQUALS_COMPARER>
258 bool DoublyLinkedList<T>::Contains (ArgByValueType<T> item, const EQUALS_COMPARER& equalsComparer) const
259 {
260 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
261 for (const Link_* current = fHead_; current != nullptr; current = current->fNext) {
262 if (equalsComparer (current->fItem, item)) {
263 return true;
264 }
265 }
266 return false;
267 }
268 template <typename T>
269 template <invocable<T> FUNCTION>
270 inline void DoublyLinkedList<T>::Apply (FUNCTION&& doToElement) const
271 {
272 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
273 for (const Link_* i = fHead_; i != nullptr; i = i->fNext) {
274 doToElement (i->fItem);
275 }
276 }
277 template <typename T>
278 template <typename FUNCTION>
279 inline auto DoublyLinkedList<T>::Find (FUNCTION&& firstThat) const -> UnderlyingIteratorRep
280 {
281 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
282 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
283 if (firstThat (i->fItem)) {
284 return i;
285 }
286 }
287 return nullptr;
288 }
289 template <typename T>
291 {
293 for (Link_* i = fHead_; i != nullptr;) {
294 Link_* deleteMe = i;
295 i = i->fNext;
296 delete deleteMe;
297 }
298 fHead_ = nullptr;
299 fTail_ = nullptr;
300 }
301 template <typename T>
302 T DoublyLinkedList<T>::GetAt (size_t i) const
303 {
304 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
305 Require (i >= 0);
306 Require (i < size ());
307 const Link_* cur = fHead_;
308 for (; i != 0; cur = cur->fNext, --i) {
309 AssertNotNull (cur); // cuz i <= fLength
310 }
311 AssertNotNull (cur); // cuz i <= fLength
312 return (cur->fItem);
313 }
314 template <typename T>
315 void DoublyLinkedList<T>::SetAt (size_t i, ArgByValueType<T> item)
316 {
318 Require (i >= 0);
319 Require (i < size ());
320 Link_* cur = fHead_;
321 for (; i != 0; cur = cur->fNext, --i) {
322 AssertNotNull (cur); // cuz i <= fLength
323 }
324 AssertNotNull (cur); // cuz i <= fLength
325 cur->fItem = item;
326 }
327 template <typename T>
328 inline void DoublyLinkedList<T>::MoveIteratorHereAfterClone (ForwardIterator* pi, const DoublyLinkedList<T>* movedFrom) const
329 {
330 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
331 // TRICKY TODO - BUT MUST DO - MUST MOVE FROM OLD ITER TO NEW
332 // only way
333 //
334 // For STL containers, not sure how to find an equiv new iterator for an old one, but my best guess is to iterate through
335 // old for old, and when I match, stop on new
336#if qStroika_Foundation_Debug_AssertionsChecked
337 Require (pi->fData_ == movedFrom);
338#endif
339 auto newI = this->fHead_;
340 [[maybe_unused]] auto newE = nullptr;
341 auto oldI = movedFrom->fHead_;
342 [[maybe_unused]] auto oldE = nullptr;
343 while (oldI != pi->fCurrent_) {
344 Assert (newI != newE);
345 Assert (oldI != oldE);
346 newI = newI->fNext;
347 oldI = oldI->fNext;
348 Assert (newI != newE);
349 Assert (oldI != oldE);
350 }
351 Assert (oldI == pi->fCurrent_);
352 pi->fCurrent_ = newI;
353#if qStroika_Foundation_Debug_AssertionsChecked
354 pi->fData_ = this;
355#endif
356 }
357 template <typename T>
358 inline auto DoublyLinkedList<T>::begin () const -> ForwardIterator
359 {
360 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
361 return ForwardIterator{this};
362 }
363 template <typename T>
364 constexpr auto DoublyLinkedList<T>::end () const noexcept -> ForwardIterator
365 {
366 return ForwardIterator{};
367 }
368 template <typename T>
369 auto DoublyLinkedList<T>::erase (const ForwardIterator& i) -> ForwardIterator
370 {
371 ForwardIterator next = i;
372 ++next;
373 Remove (i);
374 next.Invariant ();
375 return next;
376 }
377 template <typename T>
378 void DoublyLinkedList<T>::Remove (const ForwardIterator& i)
379 {
381 Require (not i.Done ());
382#if qStroika_Foundation_Debug_AssertionsChecked
383 Require (i.fData_ == this); // assure iterator not stale
384#endif
385 this->Invariant ();
386
387 const Link_* victim = i.fCurrent_;
388 AssertNotNull (victim); // cuz not done
389 /*
390 * Before:
391 * | | | | | |
392 * | A | | V | | C |
393 * ... | <-prev | | <-prev | | <-prev | ...
394 * | next-> | | next-> | | next-> |
395 * | | | | | |
396 *
397 * After:
398 * | | | |
399 * | A | | C |
400 * ... | <-prev | | <-prev | ...
401 * | next-> | | next-> |
402 * | | | |
403 */
404 if (victim->fPrev == nullptr) {
405 // In this case 'A' does not exist - it is Nil...
406 Assert (fHead_ == victim);
407 fHead_ = victim->fNext; // 'C' is now first
408 if (fHead_ == nullptr) {
409 fTail_ = nullptr;
410 }
411 else {
412 Assert (fHead_->fPrev == victim); // Victim used to be 'C's prev
413 fHead_->fPrev = nullptr; // Now Nil!
414 }
415 }
416 else {
417 Assert (victim->fPrev->fNext == victim); // In this case 'A' DOES exist
418 victim->fPrev->fNext = victim->fNext; // Make A point to C
419 // Now make 'C' point back to A (careful if 'C' is Nil)
420 if (victim->fNext == nullptr) {
421 // In this case 'C' does not exist - it is Nil...
422 Assert (victim == fTail_);
423 fTail_ = victim->fPrev; // 'A' is now last
424 }
425 else {
426 Assert (victim->fNext->fPrev == victim); // Victim used to be 'C's prev
427 victim->fNext->fPrev = victim->fPrev; // Now 'A' is
428 }
429 }
430 delete victim;
431 this->Invariant ();
432 }
433 template <typename T>
434 inline void DoublyLinkedList<T>::SetAt (const ForwardIterator& i, ArgByValueType<T> newValue)
435 {
437 Require (not i.Done ());
438#if qStroika_Foundation_Debug_AssertionsChecked
439 Require (i.fData_ == this); // assure iterator not stale
440#endif
441 this->Invariant ();
442 const_cast<Link_*> (i.fCurrent_)->fItem = newValue;
443 this->Invariant ();
444 }
445 template <typename T>
446 void DoublyLinkedList<T>::AddBefore (const ForwardIterator& i, ArgByValueType<T> newValue)
447 {
449#if qStroika_Foundation_Debug_AssertionsChecked
450 Require (i.fData_ == this); // assure iterator not stale
451#endif
452 /*
453 * NB: This code works fine, even if we are done!!!
454 */
455 this->Invariant ();
456 if (i.fCurrent_ == nullptr) {
457 /*
458 * NB: If I am past the last item on the list, AddBefore() is equivalent
459 * to Appending to the list.
460 */
461 Assert (i.Done ());
462 push_back (newValue);
463 Assert (i.Done ()); // what is done, cannot be undone!!!
464 }
465 else {
466 Link_* prev = i.fCurrent_->fPrev;
467 if (prev == nullptr) {
468 push_front (newValue);
469 }
470 else {
471 /*
472 * | | | | | |
473 * | PREV | | NEW | | CUR |
474 * ... | <-prev | | <-prev | | <-prev | ...
475 * | next-> | | next-> | | next-> |
476 * | | | | | |
477 */
478 Assert (prev->fNext == i.fCurrent_);
479 Link_* iteratorCurLink = const_cast<Link_*> (i.fCurrent_);
480 prev->fNext = new Link_{newValue, prev, iteratorCurLink};
481 // Since fCurrent != nullptr from above, we update its prev, and don't have
482 // to worry about fTail_.
483 iteratorCurLink->fPrev = prev->fNext;
484 Assert (i.fCurrent_->fPrev->fPrev == prev); // old prev is two back now...
485 }
486 }
487 this->Invariant ();
488 }
489 template <typename T>
490 inline void DoublyLinkedList<T>::AddAfter (const ForwardIterator& i, ArgByValueType<T> newValue)
491 {
493#if qStroika_Foundation_Debug_AssertionsChecked
494 Require (i.fData_ == this); // assure iterator not stale
495#endif
496 this->Invariant ();
497 Require (not i.Done ());
498 AssertNotNull (i.fCurrent_); // since not done...
499 Assert (fHead_ != nullptr);
500 /*
501 * | | | |
502 * | CUR | | NEW |
503 * ... | <-prev | | <-prev | ...
504 * | next-> | | next-> |
505 * | | | |
506 */
507 Link_* iteratorCurLink = const_cast<Link_*> (i.fCurrent_);
508 Link_* newLink = new Link_{newValue, iteratorCurLink, iteratorCurLink->fNext};
509 iteratorCurLink->fNext = newLink;
510 if (newLink->fNext != nullptr) {
511 newLink->fNext->fPrev = newLink;
512 }
513 if (newLink->fNext == nullptr) {
514 fTail_ = newLink;
515 }
516 else {
517 Assert (newLink->fNext->fPrev == newLink); // cuz of params to new Link_...
518 }
519 this->Invariant ();
520 }
521#if qStroika_Foundation_Debug_AssertionsChecked
522 template <typename T>
523 void DoublyLinkedList<T>::Invariant_ () const noexcept
524 {
525#if qStroika_Foundation_Containers_DataStructures_DoublyLinkedList_IncludeSlowDebugChecks_
526 AssertExternallySynchronizedMutex::ReadContext declareContext{*this};
527#endif
528 if (fHead_ != nullptr) {
529 Assert (fHead_->fPrev == nullptr);
530 if (fHead_->fNext == nullptr) {
531 Assert (fHead_ == fTail_);
532 }
533 else {
534 Assert (fHead_->fNext->fPrev == fHead_);
535 }
536 }
537 if (fTail_ != nullptr) {
538 Assert (fTail_->fNext == nullptr);
539 if (fTail_->fPrev == nullptr) {
540 Assert (fHead_ == fTail_);
541 }
542 else {
543 Assert (fTail_->fPrev->fNext == fTail_);
544 }
545 }
546 Assert (fHead_ == nullptr or fHead_->fPrev == nullptr);
547 Assert (fTail_ == nullptr or fTail_->fNext == nullptr);
548
549 /*
550 * Check we are properly linked together.
551 */
552 size_t forwardCounter = 0;
553 for (Link_* i = fHead_; i != nullptr; i = i->fNext) {
554 Assert (i->fNext == nullptr or i->fNext->fPrev == i); // adjacent nodes point at each other
555 ++forwardCounter;
556 }
557 size_t backwardCounter{};
558 for (Link_* i = fTail_; i != nullptr; i = i->fPrev) {
559 Assert (i->fPrev == nullptr or i->fPrev->fNext == i); // adjacent nodes point at each other
560 ++backwardCounter;
561 }
562 Assert (forwardCounter == backwardCounter);
563 }
564#endif
565
566 /*
567 ********************************************************************************
568 ********************** DoublyLinkedList<T>::ForwardIterator ********************
569 ********************************************************************************
570 */
571 template <typename T>
572 constexpr DoublyLinkedList<T>::ForwardIterator::ForwardIterator ([[maybe_unused]] const DoublyLinkedList* data, UnderlyingIteratorRep startAt) noexcept
573 : fCurrent_{startAt}
574#if qStroika_Foundation_Debug_AssertionsChecked
575 , fData_{data}
576#endif
577 {
578 }
579 template <typename T>
580 constexpr DoublyLinkedList<T>::ForwardIterator::ForwardIterator (const DoublyLinkedList* data) noexcept
581 : ForwardIterator{data, (RequireExpression (data != nullptr), data->fHead_)}
582 {
583 }
584 template <typename T>
585 inline void DoublyLinkedList<T>::ForwardIterator::Invariant () const noexcept
586 {
587#if qStroika_Foundation_Debug_AssertionsChecked
588 Invariant_ ();
589#endif
590 }
591 template <typename T>
592 inline DoublyLinkedList<T>::ForwardIterator::operator bool () const
593 {
594 return not Done ();
595 }
596 template <typename T>
597 inline bool DoublyLinkedList<T>::ForwardIterator::Done () const noexcept
598 {
599#if qStroika_Foundation_Debug_AssertionsChecked
600 if (fData_ != nullptr) {
601 AssertExternallySynchronizedMutex::ReadContext declareContext{*fData_};
602 Invariant ();
603 }
604#endif
605 return fCurrent_ == nullptr;
606 }
607 template <typename T>
608 inline auto DoublyLinkedList<T>::ForwardIterator::operator++ () noexcept -> ForwardIterator&
609 {
610 Require (not Done ());
611 Invariant ();
612 Assert (fCurrent_ != nullptr);
613 fCurrent_ = fCurrent_->fNext;
614 Invariant ();
615 return *this;
616 }
617 template <typename T>
618 inline auto DoublyLinkedList<T>::ForwardIterator::operator++ (int) noexcept -> ForwardIterator
619 {
620 ForwardIterator result = *this;
621 this->operator++ ();
622 return result;
623 }
624 template <typename T>
625 inline const T& DoublyLinkedList<T>::ForwardIterator::operator* () const
626 {
627#if qStroika_Foundation_Debug_AssertionsChecked
628 RequireNotNull (fData_);
629 AssertExternallySynchronizedMutex::ReadContext declareContext{*fData_};
630#endif
631 Require (not Done ());
632 Invariant ();
633 AssertNotNull (fCurrent_);
634 return fCurrent_->fItem;
635 }
636 template <typename T>
637 inline const T* DoublyLinkedList<T>::ForwardIterator::operator->() const
638 {
639#if qStroika_Foundation_Debug_AssertionsChecked
640 RequireNotNull (fData_);
641 AssertExternallySynchronizedMutex::ReadContext declareContext{*fData_};
642#endif
643 Require (not Done ());
644 Invariant ();
645 AssertNotNull (fCurrent_);
646 return &fCurrent_->fItem;
647 }
648 template <typename T>
649 size_t DoublyLinkedList<T>::ForwardIterator::CurrentIndex (const DoublyLinkedList* data) const
650 {
651 Require (not Done ());
652#if qStroika_Foundation_Debug_AssertionsChecked
653 Require (data == fData_);
654 RequireNotNull (fData_);
655#endif
656 RequireNotNull (this->fCurrent_);
657 size_t i = 0;
658 for (const Link_* l = data->fHead_;; l = l->fNext, ++i) {
659 AssertNotNull (l);
660 if (l == fCurrent_) [[unlikely]] {
661 return i;
662 }
663 }
665 return i;
666 }
667 template <typename T>
668 inline auto DoublyLinkedList<T>::ForwardIterator::GetUnderlyingIteratorRep () const -> UnderlyingIteratorRep
669 {
670 return fCurrent_;
671 }
672 template <typename T>
673 inline void DoublyLinkedList<T>::ForwardIterator::SetUnderlyingIteratorRep (UnderlyingIteratorRep l)
674 {
675#if qStroika_Foundation_Debug_AssertionsChecked
676 AssertExternallySynchronizedMutex::ReadContext declareContext{*fData_}; // read lock on data, though writing to this iterator
677#endif
678 // MUST COME FROM THIS LIST
679 // CAN be nullptr
680 fCurrent_ = l;
681 }
682 template <typename T>
683 constexpr void DoublyLinkedList<T>::ForwardIterator::AssertDataMatches ([[maybe_unused]] const DoublyLinkedList* data) const
684 {
685#if qStroika_Foundation_Debug_AssertionsChecked
686 Require (data == fData_);
687#endif
688 }
689 template <typename T>
690 inline bool DoublyLinkedList<T>::ForwardIterator::operator== (const ForwardIterator& rhs) const
691 {
692 return fCurrent_ == rhs.fCurrent_;
693 }
694#if qStroika_Foundation_Debug_AssertionsChecked
695 template <typename T>
696 void DoublyLinkedList<T>::ForwardIterator::Invariant_ () const noexcept
697 {
698 }
699#endif
700
701}
#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...