@@ -602,6 +602,8 @@ namespace m
602602
603603 T* old_e = e; // save a copy so we don't have to re-load
604604
605+ // Pre-increment d's refcount so that if the CAS succeeds, m_ptr holds
606+ // a valid reference to d without a window where the refcount is zero.
605607 increment_ref (d);
606608
607609 if (m_ptr.compare_exchange_strong (e, d, std::memory_order_acq_rel))
@@ -613,8 +615,12 @@ namespace m
613615 return true ;
614616 }
615617
616- // The compare_exchange "failed", so now we need to update
617- // "expected" to the new value.
618+ // The CAS failed: m_ptr still holds its current value (now captured in `e`).
619+ // `d` was pre-incremented above but was never stored in m_ptr, so we must
620+ // undo that increment to avoid a permanent ref-count leak on `desired`.
621+ decrement_ref (d);
622+
623+ // Update `expected` to reflect the actual current value of m_ptr.
618624 expected.reset (e);
619625
620626 return false ;
@@ -692,6 +698,13 @@ namespace m
692698
693699 std::atomic<T*> m_ptr{nullptr };
694700
701+ // All specializations of arefc_ptr are friends of each other so that the
702+ // cross-type converting constructor and assignment operators can call the
703+ // private addref() / put() members on a related specialization.
704+ template <typename U>
705+ requires (arefc_ptr_requirements<U>)
706+ friend class arefc_ptr ;
707+
695708 template <typename T1, typename ... Args>
696709 requires (arefc_ptr_requirements<T1>)
697710 friend arefc_ptr<T1>
0 commit comments