diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2022-01-13 22:08:43 +0000 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2022-01-14 10:14:25 +0000 |
commit | 9a0b518a82db68c5cbd9ea8ccc47c2ff45182519 (patch) | |
tree | 6fce64cd7aacd03b1f14e5cb0282f4d917d49d23 /libstdc++-v3/include | |
parent | fc6f1128ae603164aea6303ce2b3ed0b57e6a378 (diff) | |
download | gcc-9a0b518a82db68c5cbd9ea8ccc47c2ff45182519.zip gcc-9a0b518a82db68c5cbd9ea8ccc47c2ff45182519.tar.gz gcc-9a0b518a82db68c5cbd9ea8ccc47c2ff45182519.tar.bz2 |
libstdc++: Add C++20 std::make_shared enhancements (P0674R1)
This adds the overloads of std::make_shared and std::allocate_shared for
creating arrays, added to C++20 by P0674R1.
It also adds std::make_shared_for_overwrite, added to C++20 by P1020R1
(and renamed by P1973R1). The std::make_unique_for_overwite overloads
are already supported.
The original std::make_shared overload is changed to construct a
shared_ptr directly instead of calling std::allocate_shared. This
removes a function call at runtime, and avoids having to do overload
resolution for std::allocate_shared, now that there are five overloads
of it.
Allocating a shared array is done by a new __shared_count constructor.
An array is allocated with space for additional elements at the end and
an instance of new _Sp_counted_array class template is constructed in
that unused capacity.
The non-array form of std::make_shared_for_overwrite uses the same
__shared_count constructor as the original std::make_shared overload,
but a new partial specialization of _Sp_counted_ptr_inplace is selected
when the allocator's value_type is the new _Sp_overwrite_tag type. That
new partial specialization default-initializes its contained object and
destroys it with a destructor call rather than using the allocator.
Despite being C++20 features, this implementation only uses concepts
conditionally, with workarounds when they are not supported. This allows
it to work with older non-GCC compilers (Clang 9 and icc 2021). At some
point we can simplify the code by removing the workarounds.
libstdc++-v3/ChangeLog:
* include/bits/shared_ptr.h (__cpp_lib_shared_ptr_weak_type):
Correct type of macro value.
(shared_ptr): Add additional friend declarations.
(make_shared, allocate_shared): Constrain existing overloads and
remove static_assert.
* include/bits/shared_ptr_base.h (__cpp_lib_smart_ptr_for_overwrite):
New macro.
(_Sp_counted_ptr_inplace<T, Alloc, Lp>): New partial
specialization for use with make_shared_for_overwrite.
(__cpp_lib_shared_ptr_arrays): Update value for C++20.
(_Sp_counted_array_base): New class template.
(_Sp_counted_array): New class template.
(__shared_count(_Tp*&, const _Sp_counted_array_base&, _Init)):
New constructor for allocating shared arrays.
(__shared_ptr(const _Sp_counted_array_base&, _Init)): Likewise.
* include/std/version (__cpp_lib_shared_ptr_weak_type): Correct
type.
(__cpp_lib_shared_ptr_arrays): Update value for C++20.
(__cpp_lib_smart_ptr_for_overwrite): New macro.
* testsuite/20_util/shared_ptr/creation/99006.cc: Adjust
expected errors.
* testsuite/20_util/shared_ptr/creation/array.cc: New test.
* testsuite/20_util/shared_ptr/creation/overwrite.cc: New test.
* testsuite/20_util/shared_ptr/creation/version.cc: New test.
* testsuite/20_util/unique_ptr/creation/for_overwrite.cc: Check
feature test macro. Test non-trivial default-initialization.
Diffstat (limited to 'libstdc++-v3/include')
-rw-r--r-- | libstdc++-v3/include/bits/shared_ptr.h | 271 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/shared_ptr_base.h | 299 | ||||
-rw-r--r-- | libstdc++-v3/include/std/version | 6 |
3 files changed, 562 insertions, 14 deletions
diff --git a/libstdc++-v3/include/bits/shared_ptr.h b/libstdc++-v3/include/bits/shared_ptr.h index 5fdfcb2..f44008e 100644 --- a/libstdc++-v3/include/bits/shared_ptr.h +++ b/libstdc++-v3/include/bits/shared_ptr.h @@ -100,6 +100,57 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif } + /// @cond undocumented + + // Constraint for overloads taking non-array types. +#if __cpp_concepts && __cpp_lib_type_trait_variable_templates + template<typename _Tp> + requires (!is_array_v<_Tp>) + using _NonArray = _Tp; +#else + template<typename _Tp> + using _NonArray = __enable_if_t<!is_array<_Tp>::value, _Tp>; +#endif + +#if __cpp_lib_shared_ptr_arrays >= 201707L + // Constraint for overloads taking array types with unknown bound, U[]. +#if __cpp_concepts + template<typename _Tp> + requires is_array_v<_Tp> && (extent_v<_Tp> == 0) + using _UnboundedArray = _Tp; +#else + template<typename _Tp> + using _UnboundedArray + = __enable_if_t<__is_array_unknown_bounds<_Tp>::value, _Tp>; +#endif + + // Constraint for overloads taking array types with known bound, U[N]. +#if __cpp_concepts + template<typename _Tp> + requires (extent_v<_Tp> != 0) + using _BoundedArray = _Tp; +#else + template<typename _Tp> + using _BoundedArray + = __enable_if_t<__is_array_known_bounds<_Tp>::value, _Tp>; +#endif + +#if __cpp_lib_smart_ptr_for_overwrite + // Constraint for overloads taking either non-array or bounded array, U[N]. +#if __cpp_concepts + template<typename _Tp> + requires (!is_array_v<_Tp>) || (extent_v<_Tp> != 0) + using _NotUnboundedArray = _Tp; +#else + template<typename _Tp> + using _NotUnboundedArray + = __enable_if_t<!__is_array_unknown_bounds<_Tp>::value, _Tp>; +#endif +#endif // smart_ptr_for_overwrite +#endif // shared_ptr_arrays + + /// @endcond + /** * @brief A smart pointer with reference-counted copy semantics. * @headerfile memory @@ -139,7 +190,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using element_type = typename __shared_ptr<_Tp>::element_type; #if __cplusplus >= 201703L -# define __cpp_lib_shared_ptr_weak_type 201606 +# define __cpp_lib_shared_ptr_weak_type 201606L /// The corresponding weak_ptr type for this shared_ptr /// @since C++17 using weak_type = weak_ptr<_Tp>; @@ -414,8 +465,71 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { } template<typename _Yp, typename _Alloc, typename... _Args> - friend shared_ptr<_Yp> - allocate_shared(const _Alloc& __a, _Args&&... __args); + friend shared_ptr<_NonArray<_Yp>> + allocate_shared(const _Alloc&, _Args&&...); + + template<typename _Yp, typename... _Args> + friend shared_ptr<_NonArray<_Yp>> + make_shared(_Args&&...); + +#if __cpp_lib_shared_ptr_arrays >= 201707L + // This constructor is non-standard, it is used by allocate_shared<T[]>. + template<typename _Alloc, typename _Init = const remove_extent_t<_Tp>*> + shared_ptr(const _Sp_counted_array_base<_Alloc>& __a, + _Init __init = nullptr) + : __shared_ptr<_Tp>(__a, __init) + { } + + template<typename _Yp, typename _Alloc> + friend shared_ptr<_UnboundedArray<_Yp>> + allocate_shared(const _Alloc&, size_t); + + template<typename _Yp> + friend shared_ptr<_UnboundedArray<_Yp>> + make_shared(size_t); + + template<typename _Yp, typename _Alloc> + friend shared_ptr<_UnboundedArray<_Yp>> + allocate_shared(const _Alloc&, size_t, const remove_extent_t<_Yp>&); + + template<typename _Yp> + friend shared_ptr<_UnboundedArray<_Yp>> + make_shared(size_t, const remove_extent_t<_Yp>&); + + template<typename _Yp, typename _Alloc> + friend shared_ptr<_BoundedArray<_Yp>> + allocate_shared(const _Alloc&); + + template<typename _Yp> + friend shared_ptr<_BoundedArray<_Yp>> + make_shared(); + + template<typename _Yp, typename _Alloc> + friend shared_ptr<_BoundedArray<_Yp>> + allocate_shared(const _Alloc&, const remove_extent_t<_Yp>&); + + template<typename _Yp> + friend shared_ptr<_BoundedArray<_Yp>> + make_shared(const remove_extent_t<_Yp>&); + +#if __cpp_lib_smart_ptr_for_overwrite + template<typename _Yp, typename _Alloc> + friend shared_ptr<_NotUnboundedArray<_Yp>> + allocate_shared_for_overwrite(const _Alloc&); + + template<typename _Yp> + friend shared_ptr<_NotUnboundedArray<_Yp>> + make_shared_for_overwrite(); + + template<typename _Yp, typename _Alloc> + friend shared_ptr<_UnboundedArray<_Yp>> + allocate_shared_for_overwrite(const _Alloc&, size_t); + + template<typename _Yp> + friend shared_ptr<_UnboundedArray<_Yp>> + make_shared_for_overwrite(size_t); +#endif +#endif // This constructor is non-standard, it is used by weak_ptr::lock(). shared_ptr(const weak_ptr<_Tp>& __r, std::nothrow_t) noexcept @@ -872,11 +986,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * and the new object. */ template<typename _Tp, typename _Alloc, typename... _Args> - inline shared_ptr<_Tp> + inline shared_ptr<_NonArray<_Tp>> allocate_shared(const _Alloc& __a, _Args&&... __args) { - static_assert(!is_array<_Tp>::value, "make_shared<T[]> not supported"); - return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a}, std::forward<_Args>(__args)...); } @@ -889,13 +1001,152 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * constructor of @a _Tp. */ template<typename _Tp, typename... _Args> - inline shared_ptr<_Tp> + inline shared_ptr<_NonArray<_Tp>> make_shared(_Args&&... __args) { - typedef typename std::remove_cv<_Tp>::type _Tp_nc; - return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(), - std::forward<_Args>(__args)...); + using _Alloc = allocator<void>; + _Alloc __a; + return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a}, + std::forward<_Args>(__args)...); + } + +#if __cpp_lib_shared_ptr_arrays >= 201707L + /// @cond undocumented + template<typename _Tp, typename _Alloc = allocator<void>> + auto + __make_shared_arr_tag(size_t __n, const _Alloc& __a = _Alloc()) noexcept + { + using _Up = remove_all_extents_t<_Tp>; + using _UpAlloc = __alloc_rebind<_Alloc, _Up>; + size_t __s = sizeof(remove_extent_t<_Tp>) / sizeof(_Up); + if (__builtin_mul_overflow(__s, __n, &__n)) + std::__throw_bad_array_new_length(); + return _Sp_counted_array_base<_UpAlloc>{_UpAlloc(__a), __n}; + } + /// @endcond + + template<typename _Tp, typename _Alloc> + inline shared_ptr<_UnboundedArray<_Tp>> + allocate_shared(const _Alloc& __a, size_t __n) + { + return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n, __a)); + } + + template<typename _Tp> + inline shared_ptr<_UnboundedArray<_Tp>> + make_shared(size_t __n) + { + return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n)); + } + + template<typename _Tp, typename _Alloc> + inline shared_ptr<_UnboundedArray<_Tp>> + allocate_shared(const _Alloc& __a, size_t __n, + const remove_extent_t<_Tp>& __u) + { + return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n, __a), + std::__addressof(__u)); + } + + template<typename _Tp> + inline shared_ptr<_UnboundedArray<_Tp>> + make_shared(size_t __n, const remove_extent_t<_Tp>& __u) + { + return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n), + std::__addressof(__u)); + } + + /// @cond undocumented + template<typename _Tp, typename _Alloc = allocator<void>> + auto + __make_shared_arrN_tag(const _Alloc& __a = _Alloc()) noexcept + { + using _Up = remove_all_extents_t<_Tp>; + using _UpAlloc = __alloc_rebind<_Alloc, _Up>; + size_t __n = sizeof(_Tp) / sizeof(_Up); + return _Sp_counted_array_base<_UpAlloc>{_UpAlloc(__a), __n}; + } + /// @endcond + + template<typename _Tp, typename _Alloc> + inline shared_ptr<_BoundedArray<_Tp>> + allocate_shared(const _Alloc& __a) + { + return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>(__a)); + } + + template<typename _Tp> + inline shared_ptr<_BoundedArray<_Tp>> + make_shared() + { + return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>()); + } + + template<typename _Tp, typename _Alloc> + inline shared_ptr<_BoundedArray<_Tp>> + allocate_shared(const _Alloc& __a, const remove_extent_t<_Tp>& __u) + { + return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>(__a), + std::__addressof(__u)); + } + + template<typename _Tp> + inline shared_ptr<_BoundedArray<_Tp>> + make_shared(const remove_extent_t<_Tp>& __u) + { + return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>(), + std::__addressof(__u)); + } + +#if __cpp_lib_smart_ptr_for_overwrite + template<typename _Tp, typename _Alloc> + inline shared_ptr<_NotUnboundedArray<_Tp>> + allocate_shared_for_overwrite(const _Alloc& __a) + { + if constexpr (is_array_v<_Tp>) + return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>(__a), + _Sp_overwrite_tag{}); + else + { + // Rebind the allocator to _Sp_overwrite_tag, so that the + // relevant _Sp_counted_ptr_inplace specialization is used. + using _Alloc2 = __alloc_rebind<_Alloc, _Sp_overwrite_tag>; + _Alloc2 __a2 = __a; + return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc2>{__a2}); + } + } + + template<typename _Tp> + inline shared_ptr<_NotUnboundedArray<_Tp>> + make_shared_for_overwrite() + { + if constexpr (is_array_v<_Tp>) + return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>(), + _Sp_overwrite_tag{}); + else + { + using _Alloc = allocator<_Sp_overwrite_tag>; + return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{{}}); + } + } + + template<typename _Tp, typename _Alloc> + inline shared_ptr<_UnboundedArray<_Tp>> + allocate_shared_for_overwrite(const _Alloc& __a, size_t __n) + { + return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n, __a), + _Sp_overwrite_tag{}); + } + + template<typename _Tp> + inline shared_ptr<_UnboundedArray<_Tp>> + make_shared_for_overwrite(size_t __n) + { + return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n), + _Sp_overwrite_tag{}); } +#endif // smart_ptr_for_overwrite +#endif // shared_ptr_arrays /// std::hash specialization for shared_ptr. template<typename _Tp> diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h index e9f4743..e16a925 100644 --- a/libstdc++-v3/include/bits/shared_ptr_base.h +++ b/libstdc++-v3/include/bits/shared_ptr_base.h @@ -60,8 +60,11 @@ #include <ext/aligned_buffer.h> #include <ext/atomicity.h> #include <ext/concurrence.h> -#if __cplusplus > 201703L +#if __cplusplus >= 202002L +# include <bit> // __bit_floor # include <compare> +# include <bits/align.h> // std::align +# include <bits/stl_uninitialized.h> #endif namespace std _GLIBCXX_VISIBILITY(default) @@ -447,6 +450,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline void _Sp_counted_ptr<nullptr_t, _S_atomic>::_M_dispose() noexcept { } + // FIXME: once __has_cpp_attribute(__no_unique_address__)) is true for + // all supported compilers we can greatly simplify _Sp_ebo_helper. + // N.B. unconditionally applying the attribute could change layout for + // final types, which currently cannot use EBO so have a unique address. + template<int _Nm, typename _Tp, bool __use_ebo = !__is_final(_Tp) && __is_empty(_Tp)> struct _Sp_ebo_helper; @@ -640,6 +648,233 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Impl _M_impl; }; +#if __cplusplus >= 202002L +# define __cpp_lib_smart_ptr_for_overwrite 202002L + struct _Sp_overwrite_tag { }; + + // Partial specialization used for make_shared_for_overwrite<non-array>(). + // This partial specialization is used when the allocator's value type + // is the special _Sp_overwrite_tag type. +#if __cpp_concepts + template<typename _Tp, typename _Alloc, _Lock_policy _Lp> + requires is_same_v<typename _Alloc::value_type, _Sp_overwrite_tag> + class _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> final +#else + template<typename _Tp, template<typename> class _Alloc, _Lock_policy _Lp> + class _Sp_counted_ptr_inplace<_Tp, _Alloc<_Sp_overwrite_tag>, _Lp> final +#endif + : public _Sp_counted_base<_Lp> + { + [[no_unique_address]] _Alloc _M_alloc; + + union { + _Tp _M_obj; + char _M_unused; + }; + + friend class __shared_count<_Lp>; // To be able to call _M_ptr(). + + _Tp* _M_ptr() noexcept { return std::__addressof(_M_obj); } + + public: + using __allocator_type = __alloc_rebind<_Alloc, _Sp_counted_ptr_inplace>; + + _Sp_counted_ptr_inplace(const _Alloc& __a) + : _M_alloc(__a) + { + ::new((void*)_M_ptr()) _Tp; // default-initialized, for overwrite. + } + + ~_Sp_counted_ptr_inplace() noexcept { } + + virtual void + _M_dispose() noexcept + { + _M_obj.~_Tp(); + } + + // Override because the allocator needs to know the dynamic type + virtual void + _M_destroy() noexcept + { + using pointer = typename allocator_traits<__allocator_type>::pointer; + __allocator_type __a(_M_alloc); + auto __p = pointer_traits<pointer>::pointer_to(*this); + __allocated_ptr<__allocator_type> __guard_ptr{ __a, __p }; + this->~_Sp_counted_ptr_inplace(); + } + + void* + _M_get_deleter(const std::type_info&) noexcept override + { return nullptr; } + }; +#endif // C++20 + +#if __cplusplus <= 201703L +# define __cpp_lib_shared_ptr_arrays 201611L +#else +# define __cpp_lib_shared_ptr_arrays 201707L + + struct _Sp_overwrite_tag; + + // For make_shared<T[]>, make_shared<T[N]>, allocate_shared<T[]> etc. + template<typename _Alloc> + struct _Sp_counted_array_base + { + [[no_unique_address]] _Alloc _M_alloc{}; + size_t _M_n = 0; + bool _M_overwrite = false; + + typename allocator_traits<_Alloc>::pointer + _M_alloc_array(size_t __tail) + { + return allocator_traits<_Alloc>::allocate(_M_alloc, _M_n + __tail); + } + + void + _M_dealloc_array(typename allocator_traits<_Alloc>::pointer __p, + size_t __tail) + { + allocator_traits<_Alloc>::deallocate(_M_alloc, __p, _M_n + __tail); + } + + // Init the array elements + template<typename _Init> + void + _M_init(typename allocator_traits<_Alloc>::value_type* __p, + _Init __init) + { + using _Tp = remove_pointer_t<_Init>; + using _Up = typename allocator_traits<_Alloc>::value_type; + + if constexpr (is_same_v<_Init, _Sp_overwrite_tag>) + { + std::uninitialized_default_construct_n(__p, _M_n); + _M_overwrite = true; + } + else if (__init == nullptr) + std::__uninitialized_default_n_a(__p, _M_n, _M_alloc); + else if constexpr (!is_array_v<_Tp>) + std::__uninitialized_fill_n_a(__p, _M_n, *__init, _M_alloc); + else + { + struct _Iter + { + using value_type = _Up; + using difference_type = ptrdiff_t; + using pointer = const _Up*; + using reference = const _Up&; + using iterator_category = forward_iterator_tag; + + const _Up* _M_p; + size_t _M_len; + size_t _M_pos; + + _Iter& operator++() { ++_M_pos; return *this; } + _Iter operator++(int) { auto __i(*this); ++_M_pos; return __i; } + + reference operator*() const { return _M_p[_M_pos % _M_len]; } + pointer operator->() const { return _M_p + (_M_pos % _M_len); } + + bool operator==(const _Iter& __i) const + { return _M_pos == __i._M_pos; } + }; + + _Iter __first{_S_first_elem(__init), sizeof(_Tp) / sizeof(_Up)}; + _Iter __last = __first; + __last._M_pos = _M_n; + std::__uninitialized_copy_a(__first, __last, __p, _M_alloc); + } + } + + protected: + // Destroy the array elements + void + _M_dispose_array(typename allocator_traits<_Alloc>::value_type* __p) + { + if (_M_overwrite) + std::destroy_n(__p, _M_n); + else + { + size_t __n = _M_n; + while (__n--) + allocator_traits<_Alloc>::destroy(_M_alloc, __p + __n); + } + } + + private: + template<typename _Tp> + static _Tp* + _S_first_elem(_Tp* __p) { return __p; } + + template<typename _Tp, size_t _Nm> + static auto + _S_first_elem(_Tp (*__p)[_Nm]) { return _S_first_elem(*__p); } + }; + + // Control block for make_shared<T[]>, make_shared<T[N]> etc. that will be + // placed into unused memory at the end of the array. + template<typename _Alloc, _Lock_policy _Lp> + class _Sp_counted_array final + : public _Sp_counted_base<_Lp>, _Sp_counted_array_base<_Alloc> + { + using pointer = typename allocator_traits<_Alloc>::pointer; + + pointer _M_alloc_ptr; + + auto _M_ptr() const noexcept { return std::to_address(_M_alloc_ptr); } + + friend class __shared_count<_Lp>; // To be able to call _M_ptr(). + + public: + _Sp_counted_array(const _Sp_counted_array_base<_Alloc>& __a, + pointer __p) noexcept + : _Sp_counted_array_base<_Alloc>(__a), _M_alloc_ptr(__p) + { } + + ~_Sp_counted_array() = default; + + virtual void + _M_dispose() noexcept + { + if (this->_M_n) + this->_M_dispose_array(_M_ptr()); + } + + // Override because the allocator needs to know the dynamic type + virtual void + _M_destroy() noexcept + { + _Sp_counted_array_base<_Alloc> __a = *this; + pointer __p = _M_alloc_ptr; + this->~_Sp_counted_array(); + __a._M_dealloc_array(__p, _S_tail()); + } + + // Returns the number of additional array elements that must be + // allocated in order to store a _Sp_counted_array at the end. + static constexpr size_t + _S_tail() + { + // The array elemenent type. + using _Tp = typename allocator_traits<_Alloc>::value_type; + + // The space needed to store a _Sp_counted_array object. + size_t __bytes = sizeof(_Sp_counted_array); + + // Add any padding needed for manual alignment within the buffer. + if constexpr (alignof(_Tp) < alignof(_Sp_counted_array)) + __bytes += alignof(_Sp_counted_array) - alignof(_Tp); + + return (__bytes + sizeof(_Tp) - 1) / sizeof(_Tp); + } + + void* + _M_get_deleter(const std::type_info&) noexcept override + { return nullptr; } + }; +#endif // C++20 + // The default deleter for shared_ptr<T[]> and shared_ptr<T[N]>. struct __sp_array_delete { @@ -650,12 +885,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<_Lock_policy _Lp> class __shared_count { + // Prevent _Sp_alloc_shared_tag from matching the shared_ptr(P, D) ctor. template<typename _Tp> struct __not_alloc_shared_tag { using type = void; }; template<typename _Tp> struct __not_alloc_shared_tag<_Sp_alloc_shared_tag<_Tp>> { }; +#if __cpp_lib_shared_ptr_arrays >= 201707L + template<typename _Alloc> + struct __not_alloc_shared_tag<_Sp_counted_array_base<_Alloc>> { }; +#endif + public: constexpr __shared_count() noexcept : _M_pi(0) { } @@ -727,6 +968,51 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __p = __pi->_M_ptr(); } +#if __cpp_lib_shared_ptr_arrays >= 201707L + template<typename _Tp, typename _Alloc, typename _Init> + __shared_count(_Tp*& __p, const _Sp_counted_array_base<_Alloc>& __a, + _Init __init) + { + using _Up = remove_all_extents_t<_Tp>; + static_assert(is_same_v<_Up, typename _Alloc::value_type>); + + using _Sp_ca_type = _Sp_counted_array<_Alloc, _Lp>; + const size_t __tail = _Sp_ca_type::_S_tail(); + + struct _Guarded_ptr : _Sp_counted_array_base<_Alloc> + { + typename allocator_traits<_Alloc>::pointer _M_ptr; + + _Guarded_ptr(_Sp_counted_array_base<_Alloc> __a) + : _Sp_counted_array_base<_Alloc>(__a), + _M_ptr(this->_M_alloc_array(_Sp_ca_type::_S_tail())) + { } + + ~_Guarded_ptr() + { + if (_M_ptr) + this->_M_dealloc_array(_M_ptr, _Sp_ca_type::_S_tail()); + } + }; + + _Guarded_ptr __guard{__a}; + _Up* const __raw = std::to_address(__guard._M_ptr); + __guard._M_init(__raw, __init); // might throw + + void* __c = __raw + __a._M_n; + if constexpr (alignof(_Up) < alignof(_Sp_ca_type)) + { + size_t __space = sizeof(_Up) * __tail; + __c = std::align(alignof(_Sp_ca_type), sizeof(_Sp_ca_type), + __c, __space); + } + auto __pi = ::new(__c) _Sp_ca_type(__guard, __guard._M_ptr); + __guard._M_ptr = nullptr; + _M_pi = __pi; + __p = reinterpret_cast<_Tp*>(__raw); + } +#endif + #if _GLIBCXX_USE_DEPRECATED #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" @@ -957,8 +1243,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_pi = nullptr; } -#define __cpp_lib_shared_ptr_arrays 201611L - // Helper traits for shared_ptr of array: // A pointer type Y* is said to be compatible with a pointer type T* when @@ -1420,6 +1704,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend __shared_ptr<_Tp1, _Lp1> __allocate_shared(const _Alloc& __a, _Args&&... __args); +#if __cpp_lib_shared_ptr_arrays >= 201707L + // This constructor is non-standard, it is used by allocate_shared<T[]>. + template<typename _Alloc, typename _Init = const remove_extent_t<_Tp>*> + __shared_ptr(const _Sp_counted_array_base<_Alloc>& __a, + _Init __init = nullptr) + : _M_ptr(), _M_refcount(_M_ptr, __a, __init) + { } +#endif + // This constructor is used by __weak_ptr::lock() and // shared_ptr::shared_ptr(const weak_ptr&, std::nothrow_t). __shared_ptr(const __weak_ptr<_Tp, _Lp>& __r, std::nothrow_t) noexcept diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index c5d5efb..a8b792e 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -51,8 +51,10 @@ #if _GLIBCXX_HOSTED # define __cpp_lib_allocator_traits_is_always_equal 201411 +#if __cplusplus < 201703L // N.B. updated value in C++20 # define __cpp_lib_shared_ptr_arrays 201611L #endif +#endif #if !defined(__STRICT_ANSI__) // gnu++11 @@ -167,7 +169,7 @@ # define __cpp_lib_scoped_lock 201703 # define __cpp_lib_shared_mutex 201505L #endif -#define __cpp_lib_shared_ptr_weak_type 201606 +#define __cpp_lib_shared_ptr_weak_type 201606L #define __cpp_lib_string_view 201803L #if _GLIBCXX_HAVE_USELOCALE # define __cpp_lib_to_chars 201611L @@ -275,7 +277,9 @@ #if __cpp_lib_atomic_wait || _GLIBCXX_HAVE_POSIX_SEMAPHORE # define __cpp_lib_semaphore 201907L #endif +#define __cpp_lib_shared_ptr_arrays 201707L #define __cpp_lib_shift 201806L +#define __cpp_lib_smart_ptr_for_overwrite 202002L #if __cpp_lib_concepts # define __cpp_lib_span 202002L #endif |