Implement c++20 `shared_ptr` rvalue overloads for aliasing constructor and pointer casts. See https://cplusplus.github.io/LWG/issue2996
Commit from
"patedeev" <dkp10000@gmail.com>
Reviewed By: philnik, #libc, ldionne, pateldeev
Differential Revision: https://reviews.llvm.org/D135548
"`2943 <https://wg21.link/LWG2943>`__","Problematic specification of the wide version of ``basic_filebuf::open``\ ","San Diego","|Nothing To Do|",""
"`2960 <https://wg21.link/LWG2960>`__","[fund.ts.v3] ``nonesuch``\ is insufficiently useless","San Diego","|Complete|",""
"`2995 <https://wg21.link/LWG2995>`__","``basic_stringbuf``\ default constructor forbids it from using SSO capacity","San Diego","",""
-"`2996 <https://wg21.link/LWG2996>`__","Missing rvalue overloads for ``shared_ptr``\ operations","San Diego","",""
+"`2996 <https://wg21.link/LWG2996>`__","Missing rvalue overloads for ``shared_ptr``\ operations","San Diego","|Complete|","17.0"
"`3008 <https://wg21.link/LWG3008>`__","``make_shared``\ (sub)object destruction semantics are not specified","San Diego","|Complete|","16.0"
"`3022 <https://wg21.link/LWG3022>`__","``is_convertible<derived*, base*>``\ may lead to ODR","San Diego","Resolved by 1285R0",""
"`3025 <https://wg21.link/LWG3025>`__","Map-like container deduction guides should use ``pair<Key, T>``\ , not ``pair<const Key, T>``\ ","San Diego","|Complete|",""
__cntrl_->__add_shared();
}
+// LWG-2996
+// We don't backport because it is an evolutionary change.
+#if _LIBCPP_STD_VER >= 20
+ template <class _Yp>
+ _LIBCPP_HIDE_FROM_ABI shared_ptr(shared_ptr<_Yp>&& __r, element_type* __p) noexcept
+ : __ptr_(__p),
+ __cntrl_(__r.__cntrl_) {
+ __r.__ptr_ = nullptr;
+ __r.__cntrl_ = nullptr;
+ }
+#endif
+
_LIBCPP_HIDE_FROM_ABI
shared_ptr(const shared_ptr& __r) _NOEXCEPT
: __ptr_(__r.__ptr_),
typename shared_ptr<_Tp>::element_type*>(__r.get()));
}
+// LWG-2996
+// We don't backport because it is an evolutionary change.
+#if _LIBCPP_STD_VER >= 20
+template <class _Tp, class _Up>
+_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> static_pointer_cast(shared_ptr<_Up>&& __r) noexcept {
+ return shared_ptr<_Tp>(std::move(__r), static_cast<typename shared_ptr<_Tp>::element_type*>(__r.get()));
+}
+#endif
+
template<class _Tp, class _Up>
inline _LIBCPP_INLINE_VISIBILITY
shared_ptr<_Tp>
return __p ? shared_ptr<_Tp>(__r, __p) : shared_ptr<_Tp>();
}
+// LWG-2996
+// We don't backport because it is an evolutionary change.
+#if _LIBCPP_STD_VER >= 20
+template <class _Tp, class _Up>
+_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> dynamic_pointer_cast(shared_ptr<_Up>&& __r) noexcept {
+ auto* __p = dynamic_cast<typename shared_ptr<_Tp>::element_type*>(__r.get());
+ return __p ? shared_ptr<_Tp>(std::move(__r), __p) : shared_ptr<_Tp>();
+}
+#endif
+
template<class _Tp, class _Up>
_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
const_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT
return shared_ptr<_Tp>(__r, const_cast<_RTp*>(__r.get()));
}
+// LWG-2996
+// We don't backport because it is an evolutionary change.
+#if _LIBCPP_STD_VER >= 20
+template <class _Tp, class _Up>
+_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> const_pointer_cast(shared_ptr<_Up>&& __r) noexcept {
+ return shared_ptr<_Tp>(std::move(__r), const_cast<typename shared_ptr<_Tp>::element_type*>(__r.get()));
+}
+#endif
+
template<class _Tp, class _Up>
_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
reinterpret_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT
typename shared_ptr<_Tp>::element_type*>(__r.get()));
}
+// LWG-2996
+// We don't backport because it is an evolutionary change.
+#if _LIBCPP_STD_VER >= 20
+template <class _Tp, class _Up>
+_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> reinterpret_pointer_cast(shared_ptr<_Up>&& __r) noexcept {
+ return shared_ptr<_Tp>(std::move(__r), reinterpret_cast<typename shared_ptr<_Tp>::element_type*>(__r.get()));
+}
+#endif
+
#ifndef _LIBCPP_HAS_NO_RTTI
template<class _Dp, class _Tp>
// shared_ptr
-// template<class T, class U> shared_ptr<T> const_pointer_cast(const shared_ptr<U>& r);
+// template<class T, class U> shared_ptr<T> const_pointer_cast(const shared_ptr<U>& r) noexcept;
+// template<class T, class U> shared_ptr<T> const_pointer_cast(shared_ptr<U>&& r) noexcept;
+#include <cassert>
#include <memory>
#include <type_traits>
-#include <cassert>
+#include <utility>
#include "test_macros.h"
{
{
const std::shared_ptr<const A> pA(new A);
+ ASSERT_NOEXCEPT(std::const_pointer_cast<A>(pA));
std::shared_ptr<A> pB = std::const_pointer_cast<A>(pA);
assert(pB.get() == pA.get());
assert(!pB.owner_before(pA) && !pA.owner_before(pB));
assert(!pB.owner_before(pA) && !pA.owner_before(pB));
}
#endif // TEST_STD_VER > 14
+#if TEST_STD_VER > 20
+ {
+ A* pA_raw = new A;
+ std::shared_ptr<const A> pA(pA_raw);
+ ASSERT_NOEXCEPT(std::const_pointer_cast<A>(std::move(pA)));
+ std::shared_ptr<A> pB = std::const_pointer_cast<A>(std::move(pA));
+ assert(pA.get() == nullptr);
+ assert(pB.get() == pA_raw);
+ assert(pB.use_count() == 1);
+ }
+#endif // TEST_STD_VER > 20
return 0;
}
// shared_ptr
-// template<class T, class U> shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& r);
+// template<class T, class U> shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& r) noexcept;
+// template<class T, class U> shared_ptr<T> dynamic_pointer_cast(shared_ptr<U>&& r) noexcept;
// UNSUPPORTED: no-rtti
+#include <cassert>
#include <memory>
#include <type_traits>
-#include <cassert>
+#include <utility>
#include "test_macros.h"
{
{
const std::shared_ptr<B> pB(new A);
+ ASSERT_NOEXCEPT(std::dynamic_pointer_cast<A>(pB));
std::shared_ptr<A> pA = std::dynamic_pointer_cast<A>(pB);
assert(pA.get() == pB.get());
assert(!pB.owner_before(pA) && !pA.owner_before(pB));
assert(pA.use_count() == 0);
}
#endif // TEST_STD_VER > 14
+#if TEST_STD_VER > 20
+ {
+ A* pA_raw = new A;
+ std::shared_ptr<B> pB(pA_raw);
+ ASSERT_NOEXCEPT(std::dynamic_pointer_cast<A>(std::move(pB)));
+ std::shared_ptr<A> pA = std::dynamic_pointer_cast<A>(std::move(pB));
+ assert(pB.get() == nullptr);
+ assert(pA.get() == pA_raw);
+ assert(pA.use_count() == 1);
+ }
+#endif // TEST_STD_VER > 20
return 0;
}
// template<class T, class U>
// shared_ptr<T> reinterpret_pointer_cast(const shared_ptr<U>& r) noexcept;
+// template<class T, class U>
+// shared_ptr<T> reinterpret_pointer_cast(shared_ptr<U>&& r) noexcept;
#include "test_macros.h"
+#include <cassert>
#include <memory>
#include <type_traits>
-#include <cassert>
+#include <utility>
struct A {
int x;
int main(int, char**) {
{
const std::shared_ptr<A> pA(new A);
+ ASSERT_NOEXCEPT(std::reinterpret_pointer_cast<int>(pA));
std::shared_ptr<int> pi = std::reinterpret_pointer_cast<int>(pA);
std::shared_ptr<A> pA2 = std::reinterpret_pointer_cast<A>(pi);
assert(pA2.get() == pA.get());
assert(!pi.owner_before(pA) && !pA.owner_before(pi));
}
#endif // TEST_STD_VER > 14
+#if TEST_STD_VER > 20
+ {
+ A* pA_raw = new A;
+ std::shared_ptr<A> pA(pA_raw);
+ ASSERT_NOEXCEPT(std::reinterpret_pointer_cast<int>(std::move(pA)));
+ std::shared_ptr<int> pi = std::reinterpret_pointer_cast<int>(std::move(pA));
+ assert(pA.get() == nullptr);
+ assert(pi.use_count() == 1);
+ std::shared_ptr<A> pA2 = std::reinterpret_pointer_cast<A>(std::move(pi));
+ assert(pi.get() == nullptr);
+ assert(pA2.get() == pA_raw);
+ assert(pA2.use_count() == 1);
+ }
+#endif // TEST_STD_VER > 20
return 0;
}
// shared_ptr
-// template<class T, class U> shared_ptr<T> static_pointer_cast(const shared_ptr<U>& r);
+// template<class T, class U> shared_ptr<T> static_pointer_cast(const shared_ptr<U>& r) noexcept;
+// template<class T, class U> shared_ptr<T> static_pointer_cast(shared_ptr<U>&& r) noexcept;
+#include <cassert>
#include <memory>
#include <type_traits>
-#include <cassert>
+#include <utility>
#include "test_macros.h"
{
{
const std::shared_ptr<A> pA(new A);
+ ASSERT_NOEXCEPT(std::static_pointer_cast<B>(pA));
std::shared_ptr<B> pB = std::static_pointer_cast<B>(pA);
assert(pB.get() == pA.get());
assert(!pB.owner_before(pA) && !pA.owner_before(pB));
assert(!pB.owner_before(pA) && !pA.owner_before(pB));
}
#endif // TEST_STD_VER > 14
+#if TEST_STD_VER > 20
+ {
+ A* pA_raw = new A;
+ std::shared_ptr<A> pA(pA_raw);
+ ASSERT_NOEXCEPT(std::static_pointer_cast<B>(std::move(pA)));
+ std::shared_ptr<B> pB = std::static_pointer_cast<B>(std::move(pA));
+ assert(pA.get() == nullptr);
+ assert(pB.get() == pA_raw);
+ assert(pB.use_count() == 1);
+ }
+#endif // TEST_STD_VER > 20
return 0;
}
}
#if TEST_STD_VER > 17 && defined(_LIBCPP_VERSION)
- // This won't pass when LWG-2996 is implemented.
{
std::shared_ptr<A> pA(new A);
assert(pA.use_count() == 1);
+# if TEST_STD_VER >= 20
+ // LWG-2996 is only implemented in c++20 and beyond.
+ // We don't backport because it is an evolutionary change.
{
B b;
std::shared_ptr<B> pB(std::move(pA), &b);
assert(A::count == 1);
assert(B::count == 1);
- assert(pA.use_count() == 2);
- assert(pB.use_count() == 2);
+ assert(pA.use_count() == 0);
+ assert(pB.use_count() == 1);
assert(pB.get() == &b);
}
- assert(pA.use_count() == 1);
- assert(A::count == 1);
- assert(B::count == 0);
+# endif // TEST_STD_VER > 20
}
assert(A::count == 0);
assert(B::count == 0);