diff --git a/stl/inc/memory b/stl/inc/memory index 5ee7a127acc..5b65f83450a 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -794,6 +794,40 @@ protected: } } + template + void _Weakly_convert_lvalue_avoiding_expired_conversions(const _Ptr_base<_Ty2>& _Other) noexcept { + // implement weak_ptr's copy converting ctor + if (_Other._Rep) { + _Rep = _Other._Rep; // always share ownership + _Rep->_Incwref(); + + if (_Rep->_Incref_nz()) { + _Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance + _Rep->_Decref(); + } else { + _STL_INTERNAL_CHECK(!_Ptr); + } + } else { + _STL_INTERNAL_CHECK(!_Ptr && !_Rep); + } + } + + template + void _Weakly_convert_rvalue_avoiding_expired_conversions(_Ptr_base<_Ty2>&& _Other) noexcept { + // implement weak_ptr's move converting ctor + _Rep = _Other._Rep; // always transfer ownership + _Other._Rep = nullptr; + + if (_Rep && _Rep->_Incref_nz()) { + _Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance + _Rep->_Decref(); + } else { + _STL_INTERNAL_CHECK(!_Ptr); + } + + _Other._Ptr = nullptr; + } + void _Incwref() const noexcept { if (_Rep) { _Rep->_Incwref(); @@ -2255,30 +2289,29 @@ _NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shared template class weak_ptr : public _Ptr_base<_Ty> { // class for pointer to reference counted resource public: - constexpr weak_ptr() noexcept {} // construct empty weak_ptr object + constexpr weak_ptr() noexcept {} - weak_ptr(const weak_ptr& _Other) noexcept { // construct weak_ptr object for resource pointed to by _Other - this->_Weakly_construct_from(_Other); + weak_ptr(const weak_ptr& _Other) noexcept { + this->_Weakly_construct_from(_Other); // same type, no conversion } template ::value, int> = 0> - weak_ptr(const shared_ptr<_Ty2>& _Other) noexcept { // construct weak_ptr object for resource owned by _Other - this->_Weakly_construct_from(_Other); + weak_ptr(const shared_ptr<_Ty2>& _Other) noexcept { + this->_Weakly_construct_from(_Other); // shared_ptr keeps resource alive during conversion } template ::value, int> = 0> - weak_ptr(const weak_ptr<_Ty2>& _Other) noexcept { // construct weak_ptr object for resource pointed to by _Other - this->_Weakly_construct_from(_Other.lock()); + weak_ptr(const weak_ptr<_Ty2>& _Other) noexcept { + this->_Weakly_convert_lvalue_avoiding_expired_conversions(_Other); } - weak_ptr(weak_ptr&& _Other) noexcept { // move construct from _Other + weak_ptr(weak_ptr&& _Other) noexcept { this->_Move_construct_from(_STD move(_Other)); } template ::value, int> = 0> - weak_ptr(weak_ptr<_Ty2>&& _Other) noexcept { // move construct from _Other - this->_Weakly_construct_from(_Other.lock()); - _Other.reset(); + weak_ptr(weak_ptr<_Ty2>&& _Other) noexcept { + this->_Weakly_convert_rvalue_avoiding_expired_conversions(_STD move(_Other)); } ~weak_ptr() noexcept { diff --git a/stl/inc/tuple b/stl/inc/tuple index 8cbc90d9e4d..ee6f40fb1e2 100644 --- a/stl/inc/tuple +++ b/stl/inc/tuple @@ -115,7 +115,7 @@ _INLINE_VAR constexpr bool _Tuple_nothrow_assignable_v = _Tuple_nothrow_assignable_v0 == sizeof...(_Srcs), _Dest, _Srcs...>; // STRUCT TEMPLATE _Tuple_convert_copy_val -// Constrain tuple's converting copy constructor (LWG-2549) +// Constrain tuple's copy converting constructor (LWG-2549) template struct _Tuple_convert_copy_val : true_type {}; @@ -125,7 +125,7 @@ struct _Tuple_convert_copy_val, _Uty> is_convertible&, _This>>> {}; // STRUCT TEMPLATE _Tuple_convert_move_val -// Constrain tuple's converting move constructor (LWG-2549) +// Constrain tuple's move converting constructor (LWG-2549) template struct _Tuple_convert_move_val : true_type {}; diff --git a/tests/std/tests/Dev10_851347_weak_ptr_virtual_inheritance/test.cpp b/tests/std/tests/Dev10_851347_weak_ptr_virtual_inheritance/test.cpp index 68863dd8e7d..c46ef9309f7 100644 --- a/tests/std/tests/Dev10_851347_weak_ptr_virtual_inheritance/test.cpp +++ b/tests/std/tests/Dev10_851347_weak_ptr_virtual_inheritance/test.cpp @@ -4,48 +4,109 @@ #include #include #include - using namespace std; +// Also test GH-1102 ": weak_ptr conversions don't preserve control blocks for expired objects" +template +[[nodiscard]] bool owner_equal(const weak_ptr& t, const weak_ptr& u) { + return !t.owner_before(u) && !u.owner_before(t); +} + +void test_owner_equal() { + shared_ptr sp_alive1(new int(0)); + shared_ptr sp_alive2(new int(0)); + shared_ptr sp_expiring3(new int(0)); + shared_ptr sp_expiring4(new int(0)); + + weak_ptr wp_empty; + weak_ptr wp_also_empty; + + weak_ptr wp_alive(sp_alive1); + weak_ptr wp_alive_same(sp_alive1); + weak_ptr wp_alive_different(sp_alive2); + + weak_ptr wp_expired(sp_expiring3); + weak_ptr wp_expired_same(sp_expiring3); + weak_ptr wp_expired_different(sp_expiring4); + + sp_expiring3.reset(); + sp_expiring4.reset(); + + assert(wp_empty.expired()); + assert(wp_also_empty.expired()); + + assert(!wp_alive.expired()); + assert(!wp_alive_same.expired()); + assert(!wp_alive_different.expired()); + + assert(wp_expired.expired()); + assert(wp_expired_same.expired()); + assert(wp_expired_different.expired()); + + assert(owner_equal(wp_empty, wp_also_empty)); + + assert(!owner_equal(wp_empty, wp_alive)); + assert(!owner_equal(wp_empty, wp_expired)); + + assert(!owner_equal(wp_alive, wp_empty)); + assert(!owner_equal(wp_expired, wp_empty)); + + assert(owner_equal(wp_alive, wp_alive_same)); + assert(owner_equal(wp_expired, wp_expired_same)); + + assert(!owner_equal(wp_alive, wp_alive_different)); + assert(!owner_equal(wp_alive, wp_expired)); + assert(!owner_equal(wp_expired, wp_alive)); + assert(!owner_equal(wp_expired, wp_expired_different)); +} + struct A { - int a; + int a{10}; }; -struct B : virtual public A { - int b; +struct B : virtual A { + int b{20}; }; -struct C : virtual public A { - int c; +struct C : virtual A { + int c{30}; }; -struct D : public B, public C { - int d; +struct D : B, C { + int d{40}; }; int main() { + test_owner_equal(); + shared_ptr spd(new D); - weak_ptr wpd(spd); - weak_ptr wpd2(spd); + const weak_ptr wpd_zero(spd); + weak_ptr wpd_one(spd); + weak_ptr wpd_two(spd); - spd.reset(); + weak_ptr wpa0(wpd_zero); + assert(!wpa0.expired()); + assert(owner_equal(wpa0, wpd_zero)); + assert(wpa0.lock()->a == 10); - weak_ptr wpa1(wpd); + spd.reset(); + weak_ptr wpa1(wpd_one); assert(wpa1.expired()); + assert(owner_equal(wpa1, wpd_zero)); weak_ptr wpa2; - - wpa2 = wpd; - + wpa2 = wpd_one; assert(wpa2.expired()); + assert(owner_equal(wpa2, wpd_zero)); - - weak_ptr wpa3(move(wpd)); + weak_ptr wpa3(move(wpd_one)); assert(wpa3.expired()); + assert(owner_equal(wpa3, wpd_zero)); weak_ptr wpa4; - wpa4 = move(wpd2); + wpa4 = move(wpd_two); assert(wpa4.expired()); + assert(owner_equal(wpa4, wpd_zero)); } diff --git a/tests/std/tests/P0414R2_shared_ptr_for_arrays/test.cpp b/tests/std/tests/P0414R2_shared_ptr_for_arrays/test.cpp index 26a2322fa9e..b7443129dc1 100644 --- a/tests/std/tests/P0414R2_shared_ptr_for_arrays/test.cpp +++ b/tests/std/tests/P0414R2_shared_ptr_for_arrays/test.cpp @@ -939,7 +939,7 @@ void test_LWG_2996() { assert(sp1.use_count() == 1); assert(sp1.get() == pz); - shared_ptr sp2(move(sp1)); // converting move ctor, old + shared_ptr sp2(move(sp1)); // move converting ctor, old assert(sp1.use_count() == 0); assert(sp1.get() == nullptr); assert(sp2.use_count() == 1);