// Vocabulary Types for Composite Class Design -*- C++ -*- // Copyright The GNU Toolchain Authors. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the // Free Software Foundation; either version 3, or (at your option) // any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // Under Section 7 of GPL version 3, you are granted additional // permissions described in the GCC Runtime Library Exception, version // 3.1, as published by the Free Software Foundation. // You should have received a copy of the GNU General Public License and // a copy of the GCC Runtime Library Exception along with this program; // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see // . /** @file include/bits/indirect.h * This is an internal header file, included by other library headers. * Do not attempt to use it directly. @headername{memory} */ #ifndef _GLIBCXX_INDIRECT_H #define _GLIBCXX_INDIRECT_H 1 #pragma GCC system_header #include #if __glibcxx_indirect || __glibcxx_polymorphic // >= C++26 #include #include #include #include #include // __allocate_guarded #include // allocator_arg_t #include // __is_in_place_type_v #include // hash #include // polymorphic_allocator namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __glibcxx_indirect template> class indirect; template constexpr bool __is_indirect = false; template constexpr bool __is_indirect> = true; #if _GLIBCXX_HOSTED namespace pmr { template using indirect = indirect<_Tp, polymorphic_allocator<_Tp>>; } #endif // [indirect], class template indirect template class indirect { static_assert(is_object_v<_Tp>); static_assert(!is_array_v<_Tp>); static_assert(!is_same_v<_Tp, in_place_t>); static_assert(!__is_in_place_type_v<_Tp>); static_assert(!is_const_v<_Tp> && !is_volatile_v<_Tp>); using _ATraits = allocator_traits<_Alloc>; static_assert(is_same_v<_Tp, typename _ATraits::value_type>); public: using value_type = _Tp; using allocator_type = _Alloc; using pointer = typename allocator_traits<_Alloc>::pointer; using const_pointer = typename allocator_traits<_Alloc>::const_pointer; constexpr explicit indirect() requires is_default_constructible_v<_Alloc> : _M_objp(_M_make_obj_chk()) { } constexpr explicit indirect(allocator_arg_t, const _Alloc& __a) : _M_alloc(__a), _M_objp(_M_make_obj_chk()) { } constexpr indirect(const indirect& __o) : indirect(allocator_arg, _ATraits::select_on_container_copy_construction(__o._M_alloc), __o) { } constexpr indirect(allocator_arg_t, const _Alloc& __a, const indirect& __other) : _M_alloc(__a) { if (__other._M_objp) _M_objp = _M_make_obj_chk(__other.__get()); else _M_objp = nullptr; } constexpr indirect(indirect&& __other) noexcept : _M_alloc(std::move(__other._M_alloc)), _M_objp(std::__exchange(__other._M_objp, nullptr)) { } constexpr indirect(allocator_arg_t, const _Alloc& __a, indirect&& __other) noexcept(_ATraits::is_always_equal::value) : _M_alloc(__a), _M_objp(std::__exchange(__other._M_objp, nullptr)) { if constexpr (!_ATraits::is_always_equal::value) if (_M_objp && _M_alloc != __other._M_alloc) { static_assert(sizeof(_Tp) != 0, "must be a complete type"); // _M_alloc cannot free _M_objp, give it back to __other. __other._M_objp = std::__exchange(_M_objp, nullptr); // And create a new object that can be freed by _M_alloc. _M_objp = _M_make_obj(std::move(*__other._M_objp)); } } template requires (!is_same_v, in_place_t>) && (!is_same_v, indirect>) && is_constructible_v<_Tp, _Up> && is_default_constructible_v<_Alloc> constexpr explicit indirect(_Up&& __u) : _M_objp(_M_make_obj(std::forward<_Up>(__u))) { } template requires (!is_same_v, in_place_t>) && (!is_same_v, indirect>) && is_constructible_v<_Tp, _Up> constexpr explicit indirect(allocator_arg_t, const _Alloc& __a, _Up&& __u) : _M_alloc(__a), _M_objp(_M_make_obj(std::forward<_Up>(__u))) { } template requires is_constructible_v<_Tp, _Us...> && is_default_constructible_v<_Alloc> constexpr explicit indirect(in_place_t, _Us&&... __us) : _M_objp(_M_make_obj(std::forward<_Us>(__us)...)) { } template requires is_constructible_v<_Tp, _Us...> constexpr explicit indirect(allocator_arg_t, const _Alloc& __a, in_place_t, _Us&&... __us) : _M_alloc(__a), _M_objp(_M_make_obj(std::forward<_Us>(__us)...)) { } template requires is_constructible_v<_Tp, initializer_list<_Ip>&, _Us...> && is_default_constructible_v<_Alloc> constexpr explicit indirect(in_place_t, initializer_list<_Ip> __il, _Us&&... __us) : _M_objp(_M_make_obj(__il, std::forward<_Us>(__us)...)) { } template requires is_constructible_v<_Tp, initializer_list<_Ip>&, _Us...> constexpr explicit indirect(allocator_arg_t, const _Alloc& __a, in_place_t, initializer_list<_Ip> __il, _Us&&... __us) : _M_alloc(__a), _M_objp(_M_make_obj(__il, std::forward<_Us>(__us)...)) { } constexpr ~indirect() { static_assert(sizeof(_Tp) != 0, "must be a complete type"); _M_reset(nullptr); } constexpr indirect& operator=(const indirect& __other) { static_assert(is_copy_assignable_v<_Tp>); static_assert(is_copy_constructible_v<_Tp>); if (__builtin_addressof(__other) == this) [[unlikely]] return *this; constexpr bool __pocca = _ATraits::propagate_on_container_copy_assignment::value; pointer __ptr = nullptr; if (__other._M_objp) { if (_ATraits::is_always_equal::value || _M_alloc == __other._M_alloc) { if (_M_objp) { *_M_objp = __other.__get(); if constexpr (__pocca) _M_alloc = __other._M_alloc; return *this; } } const indirect& __x = __pocca ? __other : *this; __ptr = __x._M_make_obj(__other.__get()); } _M_reset(__ptr); if constexpr (__pocca) _M_alloc = __other._M_alloc; return *this; } constexpr indirect& operator=(indirect&& __other) noexcept(_ATraits::propagate_on_container_move_assignment::value || _ATraits::is_always_equal::value) { // N5008 says is_copy_constructible_v here, but that seems wrong. // We only require move-constructible, and only for unequal allocators. if (__builtin_addressof(__other) == this) [[unlikely]] return *this; constexpr bool __pocma = _ATraits::propagate_on_container_move_assignment::value; pointer __ptr = nullptr; // _GLIBCXX_RESOLVE_LIB_DEFECTS // 4251. Move assignment for indirect unnecessarily requires copy construction if constexpr (_ATraits::is_always_equal::value || __pocma) __ptr = std::__exchange(__other._M_objp, nullptr); else if (_M_alloc == __other._M_alloc) __ptr = std::__exchange(__other._M_objp, nullptr); else if (__other._M_objp) { static_assert(is_move_constructible_v<_Tp>); __ptr = _M_make_obj(std::move(*__other._M_objp)); } _M_reset(__ptr); if constexpr (__pocma) _M_alloc = __other._M_alloc; return *this; } template requires (!is_same_v, indirect>) && is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up> constexpr indirect& operator=(_Up&& __u) { if (_M_objp == nullptr) _M_objp = _M_make_obj(std::forward<_Up>(__u)); else *_M_objp = std::forward<_Up>(__u); return *this; } template constexpr auto&& operator*(this _Self&& __self) noexcept { __glibcxx_assert(__self._M_objp != nullptr); return std::forward_like<_Self>(*((_Self)__self)._M_objp); } constexpr const_pointer operator->() const noexcept { // Do we want to enforce this? __glibcxx_assert(_M_objp != nullptr); return _M_objp; } constexpr pointer operator->() noexcept { // Do we want to enforce this? __glibcxx_assert(_M_objp != nullptr); return _M_objp; } constexpr bool valueless_after_move() const noexcept { return _M_objp == nullptr; } constexpr allocator_type get_allocator() const noexcept { return _M_alloc; } constexpr void swap(indirect& __other) noexcept(_ATraits::propagate_on_container_swap::value || _ATraits::is_always_equal::value) { using std::swap; swap(_M_objp, __other._M_objp); if constexpr (_ATraits::propagate_on_container_swap::value) swap(_M_alloc, __other._M_alloc); else if constexpr (!_ATraits::is_always_equal::value) __glibcxx_assert(_M_alloc == __other._M_alloc); } friend constexpr void swap(indirect& __lhs, indirect& __rhs) noexcept(_ATraits::propagate_on_container_swap::value || _ATraits::is_always_equal::value) { __lhs.swap(__rhs); } template requires requires (const _Tp& __t, const _Up& __u) { __t == __u; } friend constexpr bool operator==(const indirect& __lhs, const indirect<_Up, _Alloc2>& __rhs) noexcept(noexcept(*__lhs == *__rhs)) { if (!__lhs._M_objp || !__rhs._M_objp) return bool(__lhs._M_objp) == bool(__rhs._M_objp); else return __lhs.__get() == __rhs.__get(); } template requires (!__is_indirect<_Up>) // See PR c++/99599 && requires (const _Tp& __t, const _Up& __u) { __t == __u; } friend constexpr bool operator==(const indirect& __lhs, const _Up& __rhs) noexcept(noexcept(*__lhs == __rhs)) { if (!__lhs._M_objp) return false; else return __lhs.__get() == __rhs; } template friend constexpr __detail::__synth3way_t<_Tp, _Up> operator<=>(const indirect& __lhs, const indirect<_Up, _Alloc2>& __rhs) noexcept(noexcept(__detail::__synth3way(*__lhs, *__rhs))) { if (!__lhs._M_objp || !__rhs._M_objp) return bool(__lhs._M_objp) <=> bool(__rhs._M_objp); else return __detail::__synth3way(__lhs.__get(), __rhs.__get()); } template requires (!__is_indirect<_Up>) // See PR c++/99599 friend constexpr __detail::__synth3way_t<_Tp, _Up> operator<=>(const indirect& __lhs, const _Up& __rhs) noexcept(noexcept(__detail::__synth3way(*__lhs, __rhs))) { if (!__lhs._M_objp) return strong_ordering::less; else return __detail::__synth3way(__lhs.__get(), __rhs); } private: template friend class indirect; constexpr void _M_reset(pointer __ptr) noexcept { if (_M_objp) { _ATraits::destroy(_M_alloc, std::to_address(_M_objp)); _ATraits::deallocate(_M_alloc, _M_objp, 1); } _M_objp = __ptr; } template constexpr pointer _M_make_obj(_Args&&... __args) const { _Scoped_allocation __sa(_M_alloc, in_place, std::forward<_Args>(__args)...); return __sa.release(); } // Enforces is_constructible check and then calls _M_make_obj. template [[__gnu__::__always_inline__]] constexpr pointer _M_make_obj_chk(_Args&&... __args) const { static_assert(is_constructible_v<_Tp, _Args...>); return _M_make_obj(std::forward<_Args>(__args)...); } // Always-const accessor that avoids ADL for operator*. // This can be preferable to using *_M_objp because that might give _Tp&. // This can be preferable to using **this because that does ADL. [[__gnu__::__always_inline__]] constexpr const _Tp& __get() const noexcept { return *_M_objp; } [[no_unique_address]] _Alloc _M_alloc = _Alloc(); pointer _M_objp; // Pointer to the owned object. }; template indirect(_Value) -> indirect<_Value>; template indirect(allocator_arg_t, _Alloc, _Value) -> indirect<_Value, __alloc_rebind<_Alloc, _Value>>; // [indirect.hash], hash support template requires is_default_constructible_v> struct hash> { constexpr size_t operator()(const indirect<_Tp, _Alloc>& __t) const noexcept(noexcept(hash<_Tp>{}(*__t))) { // We pick an arbitrary hash for valueless indirect objects // which hopefully usual values of _Tp won't typically hash to. if (__t.valueless_after_move()) return -4444zu; return hash<_Tp>{}(*__t); } }; template struct __is_fast_hash>> : __is_fast_hash> { }; #endif // __glibcxx_indirect #if __glibcxx_polymorphic // C++26 && HOSTED template> class polymorphic; namespace pmr { template using polymorphic = polymorphic<_Tp, polymorphic_allocator<_Tp>>; } // [polymorphic], class template polymorphic template class polymorphic { static_assert(is_object_v<_Tp>); static_assert(!is_array_v<_Tp>); static_assert(!is_same_v<_Tp, in_place_t>); static_assert(!__is_in_place_type_v<_Tp>); static_assert(!is_const_v<_Tp> && !is_volatile_v<_Tp>); using _ATraits = allocator_traits<_Alloc>; static_assert(is_same_v<_Tp, typename _ATraits::value_type>); // The owned object is embedded within a control block which knows the // dynamic type and manages cloning and destroying the owned object. struct _Obj { typename _ATraits::pointer _M_objp{}; // pointer to the owned object. // A pointer to this type, e.g. _Obj* using pointer = typename _ATraits::template rebind_traits<_Obj>::pointer; enum class _Op { _Dispose = 1, _Copy = 2, _Move = 3 }; constexpr virtual pointer _M_manage(const _Alloc&, _Op, void* = nullptr) = 0; }; template struct _Obj_impl : _Obj { using _MyTraits = typename _ATraits::template rebind_traits<_Obj_impl>; using _Op = _Obj::_Op; union _Uninitialized { constexpr _Uninitialized() { } constexpr ~_Uninitialized() { } _Up _M_objp; }; _Uninitialized _M_u; template constexpr _Obj_impl(typename _MyTraits::allocator_type& __a, _Args&&... __args) { using _PtrTr = pointer_traits; _MyTraits::construct(__a, __builtin_addressof(_M_u._M_objp), std::forward<_Args>(__args)...); this->_M_objp = _PtrTr::pointer_to(_M_u._M_objp); } constexpr virtual typename _Obj::pointer _M_manage(const _Alloc& __a, _Op __op, void*) override { switch (__op) { case _Op::_Move: return _S_make_obj<_Up>(__a, std::move(_M_u._M_objp)); case _Op::_Copy: return _S_make_obj<_Up>(__a, const_cast(_M_u._M_objp)); case _Op::_Dispose: { using _PtrTr = pointer_traits; typename _MyTraits::allocator_type __a2(__a); _MyTraits::destroy(__a2, std::__addressof(_M_u._M_objp)); _MyTraits::deallocate(__a2, _PtrTr::pointer_to(*this), 1); return nullptr; } } __builtin_unreachable(); } }; // TODO: the standard permits a small-object optimization where the // owned object is nested within the std::polymorphic not on the heap. public: using value_type = _Tp; using allocator_type = _Alloc; using pointer = typename allocator_traits<_Alloc>::pointer; using const_pointer = typename allocator_traits<_Alloc>::const_pointer; constexpr explicit polymorphic() requires is_default_constructible_v<_Alloc> : polymorphic(in_place_type<_Tp>) { } constexpr explicit polymorphic(allocator_arg_t, const _Alloc& __a) : polymorphic(allocator_arg, __a, in_place_type<_Tp>) { } constexpr polymorphic(const polymorphic& __other) : polymorphic(allocator_arg, _ATraits::select_on_container_copy_construction( __other._M_alloc), __other) { } constexpr polymorphic(allocator_arg_t, const _Alloc& __a, const polymorphic& __other) : _M_alloc(__a) { if (__other._M_objp) _M_objp = __other._M_objp->_M_manage(__a, _Obj::_Op::_Copy); else _M_objp = nullptr; } constexpr polymorphic(polymorphic&& __other) noexcept : _M_alloc(std::move(__other._M_alloc)), _M_objp(std::__exchange(__other._M_objp, nullptr)) { } constexpr polymorphic(allocator_arg_t, const _Alloc& __a, polymorphic&& __other) noexcept(_ATraits::is_always_equal::value) : _M_alloc(__a), _M_objp(std::__exchange(__other._M_objp, nullptr)) { if constexpr (!_ATraits::is_always_equal::value) if (_M_objp && _M_alloc != __other._M_alloc) { // _M_alloc cannot free _M_objp, give it back to __other. __other._M_objp = std::__exchange(_M_objp, nullptr); // And create a new object that can be freed by _M_alloc. _M_objp = __other._M_objp->_M_manage(__a, _Obj::_Op::_Move); } } template> requires (!is_same_v<_UUp, polymorphic>) && (!__is_in_place_type_v<_UUp>) && derived_from<_UUp, _Tp> && is_constructible_v<_UUp, _Up> && is_copy_constructible_v<_UUp> && is_default_constructible_v<_Alloc> constexpr explicit polymorphic(_Up&& __u) : _M_objp(_M_make_obj<_UUp>(std::forward<_Up>(__u))) { } template> requires (!is_same_v<_UUp, polymorphic>) && (!__is_in_place_type_v<_UUp>) && derived_from<_UUp, _Tp> && is_constructible_v<_UUp, _Up> && is_copy_constructible_v<_UUp> constexpr explicit polymorphic(allocator_arg_t, const _Alloc& __a, _Up&& __u) : _M_alloc(__a), _M_objp(_M_make_obj<_UUp>(std::forward<_Up>(__u))) { } template requires is_same_v, _Up> && derived_from<_Up, _Tp> && is_constructible_v<_Up, _Ts...> && is_copy_constructible_v<_Up> && is_default_constructible_v<_Alloc> constexpr explicit polymorphic(in_place_type_t<_Up> __t, _Ts&&... __ts) : _M_objp(_M_make_obj<_Up>(std::forward<_Ts>(__ts)...)) { } template requires is_same_v, _Up> && derived_from<_Up, _Tp> && is_constructible_v<_Up, _Ts...> && is_copy_constructible_v<_Up> constexpr explicit polymorphic(allocator_arg_t, const _Alloc& __a, in_place_type_t<_Up>, _Ts&&... __ts) : _M_alloc(__a), _M_objp(_M_make_obj<_Up>(std::forward<_Ts>(__ts)...)) { } template requires is_same_v, _Up> && derived_from<_Up, _Tp> && is_constructible_v<_Up, initializer_list<_Ip>&, _Us...> && is_copy_constructible_v<_Up> && is_default_constructible_v<_Alloc> constexpr explicit polymorphic(in_place_type_t<_Up>, initializer_list<_Ip> __il, _Us&&... __us) : _M_objp(_M_make_obj<_Up>(__il, std::forward<_Us>(__us)...)) { } template requires is_same_v, _Up> && derived_from<_Up, _Tp> && is_constructible_v<_Up, initializer_list<_Ip>&, _Us...> && is_copy_constructible_v<_Up> constexpr explicit polymorphic(allocator_arg_t, const _Alloc& __a, in_place_type_t<_Up>, initializer_list<_Ip> __il, _Us&&... __us) : _M_alloc(__a), _M_objp(_M_make_obj<_Up>(__il, std::forward<_Us>(__us)...)) { } constexpr ~polymorphic() { static_assert(sizeof(_Tp) != 0, "must be a complete type"); _M_reset(nullptr); } constexpr polymorphic& operator=(const polymorphic& __other) { static_assert(sizeof(_Tp) != 0, "must be a complete type"); if (__builtin_addressof(__other) == this) [[unlikely]] return *this; constexpr bool __pocca = _ATraits::propagate_on_container_copy_assignment::value; typename _Obj::pointer __ptr = nullptr; if (__other._M_objp) { auto& __a = __pocca ? __other._M_alloc : _M_alloc; __ptr = __other._M_objp->_M_manage(__a, _Obj::_Op::_Copy); } _M_reset(__ptr); if constexpr (__pocca) _M_alloc = __other._M_alloc; return *this; } constexpr polymorphic& operator=(polymorphic&& __other) noexcept(_ATraits::propagate_on_container_move_assignment::value || _ATraits::is_always_equal::value) { if (__builtin_addressof(__other) == this) [[unlikely]] return *this; constexpr bool __pocma = _ATraits::propagate_on_container_move_assignment::value; typename _Obj::pointer __ptr = nullptr; // _GLIBCXX_RESOLVE_LIB_DEFECTS // 4251. Move assignment for indirect unnecessarily requires copy construction if constexpr (_ATraits::is_always_equal::value || __pocma) __ptr = std::__exchange(__other._M_objp, nullptr); else if (_M_alloc == __other._M_alloc) __ptr = std::__exchange(__other._M_objp, nullptr); else if (__other._M_objp) { static_assert(sizeof(_Tp) != 0, "must be a complete type"); __ptr = __other._M_objp->_M_manage(_M_alloc, _Obj::_Op::_Move); } _M_reset(__ptr); if constexpr (__pocma) _M_alloc = __other._M_alloc; return *this; } constexpr const _Tp& operator*() const noexcept { __glibcxx_assert(_M_objp != nullptr); return *_M_objp->_M_objp; } constexpr _Tp& operator*() noexcept { __glibcxx_assert(_M_objp != nullptr); return *_M_objp->_M_objp; } constexpr const_pointer operator->() const noexcept { __glibcxx_assert(_M_objp != nullptr); return _M_objp ? _M_objp->_M_objp : const_pointer{}; } constexpr pointer operator->() noexcept { __glibcxx_assert(_M_objp != nullptr); return _M_objp ? _M_objp->_M_objp : pointer{}; } constexpr bool valueless_after_move() const noexcept { return _M_objp == nullptr; } constexpr allocator_type get_allocator() const noexcept { return _M_alloc; } constexpr void swap(polymorphic& __other) noexcept(_ATraits::propagate_on_container_swap::value || _ATraits::is_always_equal::value) { using std::swap; swap(_M_objp, __other._M_objp); if constexpr (_ATraits::propagate_on_container_swap::value) swap(_M_alloc, __other._M_alloc); else if constexpr (!_ATraits::is_always_equal::value) __glibcxx_assert(_M_alloc == __other._M_alloc); } friend constexpr void swap(polymorphic& __lhs, polymorphic& __rhs) noexcept(_ATraits::propagate_on_container_swap::value || _ATraits::is_always_equal::value) { __lhs.swap(__rhs); } private: template static constexpr typename _Obj::pointer _S_make_obj(const _Alloc& __a, _Args&&... __args) { __alloc_rebind<_Alloc, _Obj_impl<_Up>> __objalloc(__a); _Scoped_allocation __sa(__objalloc, in_place, __objalloc, std::forward<_Args>(__args)...); auto __obj = __sa.release(); // FIXME: We need to downcast from _Obj_impl* to _Obj* but the // the pointer_traits usage breaks in constexpr. PR c++/110714 if constexpr (is_pointer_v) return __obj; else return pointer_traits::pointer_to(*__obj); } template constexpr typename _Obj::pointer _M_make_obj(_Args&&... __args) const { return _S_make_obj<_Up>(_M_alloc, std::forward<_Args>(__args)...); } constexpr void _M_reset(typename _Obj::pointer __ptr) noexcept { if (_M_objp) _M_objp->_M_manage(_M_alloc, _Obj::_Op::_Dispose); _M_objp = __ptr; } [[no_unique_address]] _Alloc _M_alloc = _Alloc(); typename _Obj::pointer _M_objp; }; #endif // __glibcxx_polymorphic _GLIBCXX_END_NAMESPACE_VERSION } // namespace #endif // C++26 __glibcxx_indirect || __glibcxx_polymorphic #endif // _GLIBCXX_INDIRECT_H