mirror of
https://gcc.gnu.org/git/gcc.git
synced 2026-02-22 03:46:53 -05:00
Forr rvalues the _Self parameter deduces a non-reference type. Consequently, ((_Self)__self) moved the object to a temporary, which then destroyed on function exit. This patch fixes this by using a C-style cast __self to (const indirect&). This not only resolves the above issue but also correctly handles types that are derived (publicly and privately) from indirect. Allocator requirements in [allocator.requirements.general] p22 guarantee that dereferencing const _M_objp works with equivalent semantics to dereferencing _M_objp. PR libstdc++/121128 libstdc++-v3/ChangeLog: * include/bits/indirect.h (indirect::operator*): Cast __self to approparietly qualified indirect. * testsuite/std/memory/indirect/access.cc: New test. * testsuite/std/memory/polymorphic/access.cc: New test. Reviewed-by: Jonathan Wakely <jwakely@redhat.com> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
837 lines
25 KiB
C++
837 lines
25 KiB
C++
// 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
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
/** @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 <bits/version.h>
|
|
|
|
#if __glibcxx_indirect || __glibcxx_polymorphic // >= C++26
|
|
#include <compare>
|
|
#include <initializer_list>
|
|
#include <bits/allocator.h>
|
|
#include <bits/alloc_traits.h>
|
|
#include <bits/allocated_ptr.h> // __allocate_guarded
|
|
#include <bits/uses_allocator.h> // allocator_arg_t
|
|
#include <bits/utility.h> // __is_in_place_type_v
|
|
#include <bits/functional_hash.h> // hash
|
|
#include <bits/memory_resource.h> // polymorphic_allocator
|
|
|
|
namespace std _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|
|
|
#if __glibcxx_indirect
|
|
template<typename _Tp, typename _Alloc = allocator<_Tp>>
|
|
class indirect;
|
|
|
|
template<typename _Tp>
|
|
constexpr bool __is_indirect = false;
|
|
template<typename _Tp, typename _Alloc>
|
|
constexpr bool __is_indirect<indirect<_Tp, _Alloc>> = true;
|
|
|
|
#if _GLIBCXX_HOSTED
|
|
namespace pmr
|
|
{
|
|
template<typename _Tp>
|
|
using indirect = indirect<_Tp, polymorphic_allocator<_Tp>>;
|
|
}
|
|
#endif
|
|
|
|
// [indirect], class template indirect
|
|
template<typename _Tp, typename _Alloc>
|
|
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<typename _Up = _Tp>
|
|
requires (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
|
|
&& (!is_same_v<remove_cvref_t<_Up>, 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<typename _Up = _Tp>
|
|
requires (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
|
|
&& (!is_same_v<remove_cvref_t<_Up>, 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<typename... _Us>
|
|
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<typename... _Us>
|
|
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<typename _Ip, typename... _Us>
|
|
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<typename _Ip, typename... _Us>
|
|
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<T> 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<typename _Up = _Tp>
|
|
requires (!is_same_v<remove_cvref_t<_Up>, 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<typename _Self>
|
|
constexpr auto&&
|
|
operator*(this _Self&& __self) noexcept
|
|
{
|
|
// n.b. [allocator.requirements.general] p22 implies
|
|
// dereferencing const pointer is same as pointer
|
|
const indirect& __iself = (const indirect&)__self;
|
|
__glibcxx_assert(__iself._M_objp != nullptr);
|
|
return std::forward_like<_Self>(*__iself._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<typename _Up, typename _Alloc2>
|
|
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<typename _Up>
|
|
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<typename _Up, typename _Alloc2>
|
|
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<typename _Up>
|
|
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<typename, typename> 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<typename... _Args>
|
|
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<typename... _Args>
|
|
[[__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<typename _Value>
|
|
indirect(_Value) -> indirect<_Value>;
|
|
|
|
template<typename _Alloc, typename _Value>
|
|
indirect(allocator_arg_t, _Alloc, _Value)
|
|
-> indirect<_Value, __alloc_rebind<_Alloc, _Value>>;
|
|
|
|
// [indirect.hash], hash support
|
|
template<typename _Tp, typename _Alloc>
|
|
requires is_default_constructible_v<hash<_Tp>>
|
|
struct hash<indirect<_Tp, _Alloc>>
|
|
{
|
|
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<typename _Tp, typename _Alloc>
|
|
struct __is_fast_hash<hash<indirect<_Tp, _Alloc>>>
|
|
: __is_fast_hash<hash<_Tp>>
|
|
{ };
|
|
#endif // __glibcxx_indirect
|
|
|
|
#if __glibcxx_polymorphic // C++26 && HOSTED
|
|
template<typename _Tp, typename _Alloc = allocator<_Tp>>
|
|
class polymorphic;
|
|
|
|
namespace pmr
|
|
{
|
|
template<typename _Tp>
|
|
using polymorphic = polymorphic<_Tp, polymorphic_allocator<_Tp>>;
|
|
}
|
|
|
|
// [polymorphic], class template polymorphic
|
|
template<typename _Tp, typename _Alloc>
|
|
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<typename _Up>
|
|
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<typename... _Args>
|
|
constexpr
|
|
_Obj_impl(typename _MyTraits::allocator_type& __a,
|
|
_Args&&... __args)
|
|
{
|
|
using _PtrTr = pointer_traits<typename _ATraits::pointer>;
|
|
_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<const _Up&>(_M_u._M_objp));
|
|
case _Op::_Dispose:
|
|
{
|
|
using _PtrTr = pointer_traits<typename _MyTraits::pointer>;
|
|
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<typename _Up = _Tp, typename _UUp = remove_cvref_t<_Up>>
|
|
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<typename _Up = _Tp, typename _UUp = remove_cvref_t<_Up>>
|
|
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<typename _Up, typename... _Ts>
|
|
requires is_same_v<remove_cvref_t<_Up>, _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<typename _Up, typename... _Ts>
|
|
requires is_same_v<remove_cvref_t<_Up>, _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<typename _Up, typename _Ip, typename... _Us>
|
|
requires is_same_v<remove_cvref_t<_Up>, _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<typename _Up, typename _Ip, typename... _Us>
|
|
requires is_same_v<remove_cvref_t<_Up>, _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<typename _Up, typename... _Args>
|
|
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<U>* to _Obj* but the
|
|
// the pointer_traits usage breaks in constexpr. PR c++/110714
|
|
if constexpr (is_pointer_v<typename _Obj::pointer>)
|
|
return __obj;
|
|
else
|
|
return pointer_traits<typename _Obj::pointer>::pointer_to(*__obj);
|
|
}
|
|
|
|
template<typename _Up, typename... _Args>
|
|
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
|