mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
libstdc++: Implement C++26 std::indirect [PR119152]
This patch implements C++26 std::indirect as specified in P3019 with amendment to move assignment from LWG 4251. PR libstdc++/119152 libstdc++-v3/ChangeLog: * doc/doxygen/stdheader.cc: Added indirect.h file. * include/Makefile.am: Add new header. * include/Makefile.in: Regenerate. * include/bits/indirect.h: New file. * include/bits/version.def (indirect): Define. * include/bits/version.h: Regenerate. * include/std/memory: Include new header. * testsuite/std/memory/indirect/copy.cc * testsuite/std/memory/indirect/copy_alloc.cc * testsuite/std/memory/indirect/ctor.cc * testsuite/std/memory/indirect/incomplete.cc * testsuite/std/memory/indirect/invalid_neg.cc * testsuite/std/memory/indirect/move.cc * testsuite/std/memory/indirect/move_alloc.cc * testsuite/std/memory/indirect/relops.cc Co-authored-by: Tomasz Kamiński <tkaminsk@redhat.com> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
This commit is contained in:
committed by
Tomasz Kamiński
parent
545433e9bd
commit
caf804b179
@@ -106,6 +106,7 @@ void init_map()
|
||||
headers["uses_allocator.h"] = "memory";
|
||||
headers["uses_allocator_args.h"] = "memory";
|
||||
headers["out_ptr.h"] = "memory";
|
||||
headers["indirect.h"] = "memory";
|
||||
headers["memory_resource.h"] = "memory_resource";
|
||||
headers["unique_lock.h"] = "mutex";
|
||||
headers["sat_arith.h"] = "numeric";
|
||||
|
||||
@@ -211,6 +211,7 @@ bits_headers = \
|
||||
${bits_srcdir}/gslice_array.h \
|
||||
${bits_srcdir}/hashtable.h \
|
||||
${bits_srcdir}/hashtable_policy.h \
|
||||
${bits_srcdir}/indirect.h \
|
||||
${bits_srcdir}/indirect_array.h \
|
||||
${bits_srcdir}/ios_base.h \
|
||||
${bits_srcdir}/istream.tcc \
|
||||
|
||||
@@ -564,6 +564,7 @@ bits_freestanding = \
|
||||
@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/gslice_array.h \
|
||||
@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/hashtable.h \
|
||||
@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/hashtable_policy.h \
|
||||
@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/indirect.h \
|
||||
@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/indirect_array.h \
|
||||
@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/ios_base.h \
|
||||
@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/istream.tcc \
|
||||
|
||||
459
libstdc++-v3/include/bits/indirect.h
Normal file
459
libstdc++-v3/include/bits/indirect.h
Normal file
@@ -0,0 +1,459 @@
|
||||
// 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
|
||||
{
|
||||
__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<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
|
||||
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
} // namespace
|
||||
#endif // C++26 __glibcxx_indirect || __glibcxx_polymorphic
|
||||
|
||||
#endif // _GLIBCXX_INDIRECT_H
|
||||
@@ -1969,6 +1969,15 @@ ftms = {
|
||||
};
|
||||
};
|
||||
|
||||
ftms = {
|
||||
name = indirect;
|
||||
values = {
|
||||
v = 202502;
|
||||
cxxmin = 26;
|
||||
hosted = yes;
|
||||
};
|
||||
};
|
||||
|
||||
// Standard test specifications.
|
||||
stds[97] = ">= 199711L";
|
||||
stds[03] = ">= 199711L";
|
||||
|
||||
@@ -2203,4 +2203,14 @@
|
||||
#endif /* !defined(__cpp_lib_modules) && defined(__glibcxx_want_modules) */
|
||||
#undef __glibcxx_want_modules
|
||||
|
||||
#if !defined(__cpp_lib_indirect)
|
||||
# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED
|
||||
# define __glibcxx_indirect 202502L
|
||||
# if defined(__glibcxx_want_all) || defined(__glibcxx_want_indirect)
|
||||
# define __cpp_lib_indirect 202502L
|
||||
# endif
|
||||
# endif
|
||||
#endif /* !defined(__cpp_lib_indirect) && defined(__glibcxx_want_indirect) */
|
||||
#undef __glibcxx_want_indirect
|
||||
|
||||
#undef __glibcxx_want_all
|
||||
|
||||
@@ -97,6 +97,10 @@
|
||||
# include <bits/out_ptr.h>
|
||||
#endif
|
||||
|
||||
#if __cplusplus > 202302L
|
||||
# include <bits/indirect.h>
|
||||
#endif
|
||||
|
||||
#define __glibcxx_want_addressof_constexpr
|
||||
#define __glibcxx_want_allocator_traits_is_always_equal
|
||||
#define __glibcxx_want_assume_aligned
|
||||
@@ -105,6 +109,7 @@
|
||||
#define __glibcxx_want_constexpr_dynamic_alloc
|
||||
#define __glibcxx_want_constexpr_memory
|
||||
#define __glibcxx_want_enable_shared_from_this
|
||||
#define __glibcxx_want_indirect
|
||||
#define __glibcxx_want_make_unique
|
||||
#define __glibcxx_want_out_ptr
|
||||
#define __glibcxx_want_parallel_algorithm
|
||||
|
||||
121
libstdc++-v3/testsuite/std/memory/indirect/copy.cc
Normal file
121
libstdc++-v3/testsuite/std/memory/indirect/copy.cc
Normal file
@@ -0,0 +1,121 @@
|
||||
// { dg-do run { target c++26 } }
|
||||
|
||||
#include <memory>
|
||||
#include <scoped_allocator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <testsuite_hooks.h>
|
||||
#include <testsuite_allocator.h>
|
||||
|
||||
using __gnu_test::tracker_allocator;
|
||||
using Counter = __gnu_test::tracker_allocator_counter;
|
||||
using Vector = std::vector<int>;
|
||||
using Indirect = std::indirect<Vector, tracker_allocator<Vector>>;
|
||||
const Indirect src(std::in_place, {1, 2, 3});
|
||||
|
||||
constexpr void
|
||||
test_ctor()
|
||||
{
|
||||
Counter::reset();
|
||||
Indirect i1(src);
|
||||
VERIFY( *i1 == *src );
|
||||
VERIFY( &*i1 != &*src );
|
||||
VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 1 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
|
||||
Counter::reset();
|
||||
Indirect i2(std::allocator_arg, {}, src);
|
||||
VERIFY( *i2 == *src );
|
||||
VERIFY( &*i2 != &*src );
|
||||
VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 1 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_assign()
|
||||
{
|
||||
Indirect i1;
|
||||
Counter::reset();
|
||||
|
||||
i1 = src;
|
||||
VERIFY( *i1 == *src );
|
||||
VERIFY( &*i1 != &*src );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
|
||||
auto(std::move(i1));
|
||||
Counter::reset();
|
||||
|
||||
i1 = src;
|
||||
VERIFY( *i1 == *src );
|
||||
VERIFY( &*i1 != &*src );
|
||||
VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 1 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_valueless()
|
||||
{
|
||||
Indirect e;
|
||||
auto(std::move(e));
|
||||
VERIFY( e.valueless_after_move() );
|
||||
|
||||
Counter::reset();
|
||||
Indirect i1(e);
|
||||
VERIFY( i1.valueless_after_move() );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
|
||||
Indirect i2(std::allocator_arg, {}, e);
|
||||
VERIFY( i2.valueless_after_move() );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
|
||||
Indirect i3(src);
|
||||
Counter::reset();
|
||||
i3 = e;
|
||||
VERIFY( i3.valueless_after_move() );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 1 );
|
||||
|
||||
Counter::reset();
|
||||
i3 = e;
|
||||
VERIFY( i3.valueless_after_move() );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_all()
|
||||
{
|
||||
test_ctor();
|
||||
test_assign();
|
||||
test_valueless();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_all();
|
||||
|
||||
static_assert([] {
|
||||
test_all();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
228
libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc
Normal file
228
libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc
Normal file
@@ -0,0 +1,228 @@
|
||||
// { dg-do run { target c++26 } }
|
||||
|
||||
#include <memory>
|
||||
#include <scoped_allocator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <testsuite_hooks.h>
|
||||
#include <testsuite_allocator.h>
|
||||
|
||||
using __gnu_test::propagating_allocator;
|
||||
using __gnu_test::tracker_allocator;
|
||||
using Counter = __gnu_test::tracker_allocator_counter;
|
||||
|
||||
template<bool Propagate>
|
||||
constexpr void
|
||||
test_ctor()
|
||||
{
|
||||
using PropAlloc = propagating_allocator<int, Propagate>;
|
||||
using Vector = std::vector<int, PropAlloc>;
|
||||
using ScopedAlloc = std::scoped_allocator_adaptor<
|
||||
propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
|
||||
PropAlloc>;
|
||||
using Indirect = std::indirect<Vector, ScopedAlloc>;
|
||||
|
||||
const Indirect src(std::allocator_arg, ScopedAlloc{11, 22},
|
||||
std::in_place, {1, 2, 3});
|
||||
|
||||
Counter::reset();
|
||||
Indirect i1(src);
|
||||
VERIFY( *i1 == *src );
|
||||
VERIFY( &*i1 != &*src );
|
||||
if (Propagate)
|
||||
{
|
||||
VERIFY( i1->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i1.get_allocator().get_personality() == 11 );
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY( i1->get_allocator().get_personality() == 0 );
|
||||
VERIFY( i1.get_allocator().get_personality() == 0 );
|
||||
}
|
||||
VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 1 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
|
||||
|
||||
Counter::reset();
|
||||
Indirect i2(std::allocator_arg, ScopedAlloc{33, 44}, src);
|
||||
VERIFY( *i2 == *src );
|
||||
VERIFY( &*i2 != &*src );
|
||||
VERIFY( i2->get_allocator().get_personality() == 44 );
|
||||
VERIFY( i2.get_allocator().get_personality() == 33 );
|
||||
VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 1 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
}
|
||||
|
||||
template<bool Propagate>
|
||||
constexpr void
|
||||
test_assign()
|
||||
{
|
||||
using PropAlloc = propagating_allocator<int, Propagate>;
|
||||
using Vector = std::vector<int, PropAlloc>;
|
||||
using ScopedAlloc = std::scoped_allocator_adaptor<
|
||||
propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
|
||||
PropAlloc>;
|
||||
using Indirect = std::indirect<Vector, ScopedAlloc>;
|
||||
|
||||
const Indirect src(std::allocator_arg, ScopedAlloc{11, 22},
|
||||
std::in_place, {1, 2, 3});
|
||||
|
||||
Indirect i1(std::allocator_arg, ScopedAlloc{11, 22});
|
||||
Counter::reset();
|
||||
|
||||
i1 = src;
|
||||
VERIFY( *i1 == *src );
|
||||
VERIFY( &*i1 != &*src );
|
||||
VERIFY( i1->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i1.get_allocator().get_personality() == 11 );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
|
||||
Indirect i2(std::allocator_arg, ScopedAlloc{33, 44});
|
||||
Counter::reset();
|
||||
|
||||
i2 = src;
|
||||
VERIFY( *i2 == *src );
|
||||
VERIFY( &*i2 != &*src );
|
||||
if (Propagate)
|
||||
{
|
||||
VERIFY( i2->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i2.get_allocator().get_personality() == 11 );
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY( i2->get_allocator().get_personality() == 44 );
|
||||
VERIFY( i2.get_allocator().get_personality() == 33 );
|
||||
}
|
||||
VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_construct_count() == 1 );
|
||||
VERIFY( Counter::get_destruct_count() == 1 );
|
||||
|
||||
Indirect i3(std::allocator_arg, ScopedAlloc{11, 22});
|
||||
auto(std::move(i3));
|
||||
Counter::reset();
|
||||
|
||||
i3 = src;
|
||||
VERIFY( *i3 == *src );
|
||||
VERIFY( &*i3 != &*src );
|
||||
VERIFY( i3->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i3.get_allocator().get_personality() == 11 );
|
||||
VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 1 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
|
||||
Indirect i4(std::allocator_arg, ScopedAlloc{33, 44});
|
||||
auto(std::move(i4));
|
||||
Counter::reset();
|
||||
|
||||
i4 = src;
|
||||
VERIFY( *i4 == *src );
|
||||
VERIFY( &*i4 != &*src );
|
||||
if (Propagate)
|
||||
{
|
||||
VERIFY( i4->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i4.get_allocator().get_personality() == 11 );
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY( i4->get_allocator().get_personality() == 44 );
|
||||
VERIFY( i4.get_allocator().get_personality() == 33 );
|
||||
}
|
||||
VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 1 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
}
|
||||
|
||||
template<bool Propagate>
|
||||
constexpr void
|
||||
test_valueless()
|
||||
{
|
||||
using PropAlloc = propagating_allocator<int, Propagate>;
|
||||
using Vector = std::vector<int, PropAlloc>;
|
||||
using ScopedAlloc = std::scoped_allocator_adaptor<
|
||||
propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
|
||||
PropAlloc>;
|
||||
using Indirect = std::indirect<Vector, ScopedAlloc>;
|
||||
|
||||
Indirect e(std::allocator_arg, ScopedAlloc{11, 22});
|
||||
auto(std::move(e));
|
||||
VERIFY( e.valueless_after_move() );
|
||||
|
||||
Counter::reset();
|
||||
Indirect i1(e);
|
||||
VERIFY( i1.valueless_after_move() );
|
||||
if (Propagate)
|
||||
VERIFY( i1.get_allocator().get_personality() == 11 );
|
||||
else
|
||||
VERIFY( i1.get_allocator().get_personality() == 0 );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
|
||||
Counter::reset();
|
||||
Indirect i2(std::allocator_arg, ScopedAlloc{33, 44}, e);
|
||||
VERIFY( i2.valueless_after_move() );
|
||||
VERIFY( i2.get_allocator().get_personality() == 33 );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
|
||||
Indirect i3(std::allocator_arg, ScopedAlloc{33, 44});
|
||||
Counter::reset();
|
||||
|
||||
i3 = e;
|
||||
VERIFY( i3.valueless_after_move() );
|
||||
if (Propagate)
|
||||
VERIFY( i3.get_allocator().get_personality() == 11 );
|
||||
else
|
||||
VERIFY( i3.get_allocator().get_personality() == 33 );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 1 );
|
||||
|
||||
Counter::reset();
|
||||
i2 = e;
|
||||
VERIFY( i2.valueless_after_move() );
|
||||
if (Propagate)
|
||||
VERIFY( i2.get_allocator().get_personality() == 11 );
|
||||
else
|
||||
VERIFY( i2.get_allocator().get_personality() == 33 );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
}
|
||||
|
||||
template<bool Propagate>
|
||||
constexpr void
|
||||
test_all()
|
||||
{
|
||||
test_ctor<Propagate>();
|
||||
test_assign<Propagate>();
|
||||
test_valueless<Propagate>();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_all<true>();
|
||||
test_all<false>();
|
||||
|
||||
static_assert([] {
|
||||
test_all<true>();
|
||||
test_all<false>();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
203
libstdc++-v3/testsuite/std/memory/indirect/ctor.cc
Normal file
203
libstdc++-v3/testsuite/std/memory/indirect/ctor.cc
Normal file
@@ -0,0 +1,203 @@
|
||||
// { dg-do run { target c++26 } }
|
||||
|
||||
#include <memory>
|
||||
#include <scoped_allocator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifndef __cpp_lib_indirect
|
||||
# error __cpp_lib_indirect feature test macro missing in <memory>
|
||||
#elif __cpp_lib_indirect != 202502
|
||||
# error __cpp_lib_indirect feature test macro has wrong value in <memory>
|
||||
#endif
|
||||
|
||||
#include <testsuite_hooks.h>
|
||||
#include <testsuite_allocator.h>
|
||||
|
||||
using __gnu_test::uneq_allocator;
|
||||
using UneqAlloc = uneq_allocator<int>;
|
||||
using ScopedAlloc = std::scoped_allocator_adaptor<
|
||||
uneq_allocator<std::vector<int, UneqAlloc>>,
|
||||
UneqAlloc>;
|
||||
|
||||
struct Obj
|
||||
{
|
||||
int i;
|
||||
char c[2];
|
||||
};
|
||||
|
||||
constexpr void
|
||||
test_deduction_guides()
|
||||
{
|
||||
const Obj o{};
|
||||
std::indirect i1(o);
|
||||
static_assert(std::is_same_v<decltype(i1), std::indirect<Obj>>);
|
||||
|
||||
using Alloc = __gnu_test::SimpleAllocator<Obj>;
|
||||
Alloc a;
|
||||
std::indirect i2(std::allocator_arg, a, o);
|
||||
static_assert(std::is_same_v<decltype(i2), std::indirect<Obj, Alloc>>);
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_default_ctor()
|
||||
{
|
||||
using __gnu_test::default_init_allocator;
|
||||
|
||||
std::indirect<Obj, default_init_allocator<Obj>> i1;
|
||||
default_init_allocator<int> a{};
|
||||
|
||||
// The contained object and the allocator should be value-initialized.
|
||||
VERIFY( i1->i == 0 );
|
||||
VERIFY( i1->c[0] == 0 );
|
||||
VERIFY( i1->c[1] == 0 );
|
||||
VERIFY( i1.get_allocator() == a );
|
||||
|
||||
a.state = 5;
|
||||
// Allocator-extended default constructor:
|
||||
std::indirect<Obj, default_init_allocator<Obj>> i2(std::allocator_arg, a);
|
||||
VERIFY( i2.get_allocator() == a );
|
||||
|
||||
// Object is constructed using allocator-aware constructor.
|
||||
std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
|
||||
i3(std::allocator_arg, ScopedAlloc(11, 22));
|
||||
VERIFY( i3->empty() );
|
||||
VERIFY( i3->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i3.get_allocator().get_personality() == 11 );
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_forwarding_ctor()
|
||||
{
|
||||
Obj obj{1, {'2', '3'}};
|
||||
auto verify = [](std::indirect<Obj> const& i)
|
||||
{
|
||||
VERIFY( i->i == 1 );
|
||||
VERIFY( i->c[0] == '2' );
|
||||
VERIFY( i->c[1] == '3' );
|
||||
};
|
||||
|
||||
std::indirect<Obj> i1(std::as_const(obj));
|
||||
verify(i1);
|
||||
std::indirect<Obj> i2(std::move(std::as_const(obj)));
|
||||
verify(i2);
|
||||
std::indirect<Obj> i3(obj);
|
||||
verify(i3);
|
||||
std::indirect<Obj> i4(std::move(obj));
|
||||
verify(i4);
|
||||
|
||||
std::indirect<Obj> i5({1, {'2', '3'}});
|
||||
verify(i5);
|
||||
|
||||
// Aggregate parens init
|
||||
std::indirect<Obj> i6(7);
|
||||
VERIFY( i6->i == 7 );
|
||||
|
||||
std::vector<int, UneqAlloc> v{1, 2, 3, 4, 5};
|
||||
// Object is constructed using allocator-aware constructor.
|
||||
std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
|
||||
i7(std::allocator_arg, ScopedAlloc(11, 22), v);
|
||||
VERIFY( i7->size() == 5 );
|
||||
VERIFY( v.size() == 5 );
|
||||
VERIFY( i7->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i7.get_allocator().get_personality() == 11 );
|
||||
|
||||
std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
|
||||
i8(std::allocator_arg, ScopedAlloc(11, 22), std::move(v));
|
||||
VERIFY( i8->size() == 5 );
|
||||
VERIFY( v.size() == 0 );
|
||||
VERIFY( i8->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i8.get_allocator().get_personality() == 11 );
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_inplace_ctor()
|
||||
{
|
||||
std::indirect<Obj> i1(std::in_place);
|
||||
VERIFY( i1->i == 0 );
|
||||
VERIFY( i1->c[0] == 0 );
|
||||
VERIFY( i1->c[1] == 0 );
|
||||
|
||||
std::indirect<Obj> i2(std::in_place, 10);
|
||||
VERIFY( i2->i == 10 );
|
||||
VERIFY( i2->c[0] == 0 );
|
||||
VERIFY( i2->c[1] == 0 );
|
||||
|
||||
std::indirect<Obj, uneq_allocator<Obj>>
|
||||
i3(std::allocator_arg, 42, std::in_place);
|
||||
VERIFY( i3->i == 0 );
|
||||
VERIFY( i3->c[0] == 0 );
|
||||
VERIFY( i3->c[1] == 0 );
|
||||
VERIFY( i3.get_allocator().get_personality() == 42 );
|
||||
|
||||
std::indirect<Obj, uneq_allocator<Obj>>
|
||||
i4(std::allocator_arg, 42, std::in_place, 10);
|
||||
VERIFY( i4->i == 10 );
|
||||
VERIFY( i4->c[0] == 0 );
|
||||
VERIFY( i4->c[1] == 0 );
|
||||
VERIFY( i4.get_allocator().get_personality() == 42 );
|
||||
|
||||
std::indirect<std::vector<int>> i5(std::in_place);
|
||||
VERIFY( i5->size() == 0 );
|
||||
VERIFY( i5->at(0) == 13 );
|
||||
|
||||
std::indirect<std::vector<int>> i6(std::in_place, 5, 13);
|
||||
VERIFY( i6->size() == 5 );
|
||||
VERIFY( i6->at(0) == 13 );
|
||||
|
||||
std::indirect<std::vector<int>> i7(std::in_place, {1, 2, 3, 4});
|
||||
VERIFY( i7->size() == 4 );
|
||||
VERIFY( i7->at(2) == 3 );
|
||||
|
||||
std::indirect<std::vector<int, UneqAlloc>>
|
||||
i8(std::in_place, UneqAlloc{42});
|
||||
VERIFY( i8->size() == 0 );
|
||||
VERIFY( i8->get_allocator().get_personality() == 42 );
|
||||
|
||||
std::indirect<std::vector<int, UneqAlloc>>
|
||||
i9(std::in_place, 5, 13, UneqAlloc{42});
|
||||
VERIFY( i9->size() == 5 );
|
||||
VERIFY( i9->at(0) == 13 );
|
||||
VERIFY( i9->get_allocator().get_personality() == 42 );
|
||||
|
||||
std::indirect<std::vector<int, UneqAlloc>>
|
||||
i10(std::in_place, {1, 2, 3, 4}, UneqAlloc{42});
|
||||
VERIFY( i10->size() == 4 );
|
||||
VERIFY( i10->at(2) == 3 );
|
||||
VERIFY( i10->get_allocator().get_personality() == 42 );
|
||||
|
||||
std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
|
||||
i14(std::allocator_arg, ScopedAlloc(11, 22),
|
||||
std::in_place);
|
||||
VERIFY( i14->size() == 0 );
|
||||
VERIFY( i14->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i14.get_allocator().get_personality() == 11 );
|
||||
|
||||
std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
|
||||
i15(std::allocator_arg, ScopedAlloc(11, 22),
|
||||
std::in_place, 5, 13);
|
||||
VERIFY( i15->size() == 5 );
|
||||
VERIFY( i15->at(0) == 13 );
|
||||
VERIFY( i15->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i15.get_allocator().get_personality() == 11 );
|
||||
|
||||
std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc>
|
||||
i16(std::allocator_arg, ScopedAlloc(11, 22),
|
||||
std::in_place, {1, 2, 3, 4});
|
||||
VERIFY( i16->size() == 4 );
|
||||
VERIFY( i16->at(2) == 3 );
|
||||
VERIFY( i16->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i16.get_allocator().get_personality() == 11 );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_default_ctor();
|
||||
test_forwarding_ctor();
|
||||
|
||||
static_assert([] {
|
||||
test_default_ctor();
|
||||
test_forwarding_ctor();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
38
libstdc++-v3/testsuite/std/memory/indirect/incomplete.cc
Normal file
38
libstdc++-v3/testsuite/std/memory/indirect/incomplete.cc
Normal file
@@ -0,0 +1,38 @@
|
||||
// { dg-do compile { target c++26 } }
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct Incomplete;
|
||||
bool operator==(const Incomplete&, const Incomplete&);
|
||||
std::strong_ordering operator<=>(const Incomplete&, const Incomplete&);
|
||||
|
||||
template<>
|
||||
struct std::hash<Incomplete>
|
||||
{
|
||||
static std::size_t operator()(const Incomplete& c);
|
||||
};
|
||||
|
||||
std::indirect<Incomplete>*
|
||||
test_move(std::indirect<Incomplete>& i1, std::indirect<Incomplete>& i2)
|
||||
{
|
||||
i2.swap(i2);
|
||||
return new std::indirect<Incomplete>(std::move(i1));
|
||||
}
|
||||
|
||||
void
|
||||
test_relops(std::indirect<Incomplete> const& i1, Incomplete const& o)
|
||||
{
|
||||
void(i1 == i1);
|
||||
void(i1 < i1);
|
||||
void(i1 >= i1);
|
||||
|
||||
void(i1 != o);
|
||||
void(i1 < o);
|
||||
}
|
||||
|
||||
void
|
||||
test_hash(std::indirect<Incomplete> const& i1)
|
||||
{
|
||||
std::hash<std::indirect<Incomplete>> h;
|
||||
h(i1);
|
||||
}
|
||||
28
libstdc++-v3/testsuite/std/memory/indirect/invalid_neg.cc
Normal file
28
libstdc++-v3/testsuite/std/memory/indirect/invalid_neg.cc
Normal file
@@ -0,0 +1,28 @@
|
||||
// { dg-do compile { target c++26 } }
|
||||
|
||||
#include <memory>
|
||||
|
||||
// In every specialization indirect<T, Allocator>, if the type
|
||||
// allocator_traits<Allocator>::value_type is not the same type as T,
|
||||
// the program is ill-formed.
|
||||
using T1 = std::indirect<int, std::allocator<long>>::value_type; // { dg-error "here" }
|
||||
|
||||
// A program that instantiates the definition of the template
|
||||
// indirect<T, Allocator> with a type for the T parameter that is
|
||||
// a non-object type, an array type, in_place_t,
|
||||
// a specialization of in_place_type_t, or a cv-qualified type is ill-formed.
|
||||
|
||||
using T2 = std::indirect<int&>::value_type; // { dg-error "here" }
|
||||
|
||||
using T3 = std::indirect<int[1]>::value_type; // { dg-error "here" }
|
||||
|
||||
using T4 = std::indirect<std::in_place_t>::value_type; // { dg-error "here" }
|
||||
|
||||
using T5 = std::indirect<std::in_place_type_t<int>>::value_type; // { dg-error "here" }
|
||||
|
||||
using T6 = std::indirect<const int>::value_type; // { dg-error "here" }
|
||||
|
||||
using T7 = std::indirect<volatile int>::value_type; // { dg-error "here" }
|
||||
|
||||
// { dg-error "static assertion failed" "" { target *-*-* } 0 }
|
||||
// { dg-prune-output "forming pointer to reference" }
|
||||
144
libstdc++-v3/testsuite/std/memory/indirect/move.cc
Normal file
144
libstdc++-v3/testsuite/std/memory/indirect/move.cc
Normal file
@@ -0,0 +1,144 @@
|
||||
// { dg-do run { target c++26 } }
|
||||
|
||||
#include <memory>
|
||||
#include <scoped_allocator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include <testsuite_hooks.h>
|
||||
#include <testsuite_allocator.h>
|
||||
|
||||
using __gnu_test::tracker_allocator;
|
||||
using Counter = __gnu_test::tracker_allocator_counter;
|
||||
using Vector = std::vector<int>;
|
||||
using Indirect = std::indirect<Vector, tracker_allocator<Vector>>;
|
||||
const Indirect val(std::in_place, {1, 2, 3});
|
||||
|
||||
constexpr void
|
||||
verifyNoAllocations()
|
||||
{
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_ctor()
|
||||
{
|
||||
std::optional<Indirect> src;
|
||||
auto make = [&src] -> Indirect&& {
|
||||
src.emplace(val);
|
||||
Counter::reset();
|
||||
return std::move(*src);
|
||||
};
|
||||
|
||||
Indirect i1(make());
|
||||
VERIFY( src->valueless_after_move() );
|
||||
VERIFY( *i1 == *val );
|
||||
verifyNoAllocations();
|
||||
|
||||
Indirect i2(std::allocator_arg, {}, make());
|
||||
VERIFY( src->valueless_after_move() );
|
||||
VERIFY( *i2 == *val );
|
||||
verifyNoAllocations();
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_assign()
|
||||
{
|
||||
std::optional<Indirect> src;
|
||||
auto make = [&src] -> Indirect&& {
|
||||
src.emplace(val);
|
||||
Counter::reset();
|
||||
return std::move(*src);
|
||||
};
|
||||
|
||||
Indirect i1;
|
||||
|
||||
i1 = make();
|
||||
VERIFY( src->valueless_after_move() );
|
||||
VERIFY( *i1 == *val );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 1 );
|
||||
|
||||
auto(std::move(i1));
|
||||
i1 = make();
|
||||
VERIFY( *i1 == *val );
|
||||
VERIFY( src->valueless_after_move() );
|
||||
verifyNoAllocations();
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_swap()
|
||||
{
|
||||
const Indirect val1(std::in_place, {1, 2, 3});
|
||||
const Indirect val2(std::in_place, {2, 4, 6});
|
||||
|
||||
Indirect i1(val1);
|
||||
Indirect i2(val2);
|
||||
Counter::reset();
|
||||
i1.swap(i2);
|
||||
VERIFY( *i2 == *val1 );
|
||||
VERIFY( *i1 == *val2 );
|
||||
verifyNoAllocations();
|
||||
|
||||
auto(std::move(i1));
|
||||
|
||||
Counter::reset();
|
||||
i1.swap(i2);
|
||||
VERIFY( *i1 == *val1 );
|
||||
VERIFY( i2.valueless_after_move() );
|
||||
verifyNoAllocations();
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_valueless()
|
||||
{
|
||||
auto e = [] {
|
||||
Indirect res;
|
||||
auto(std::move(res));
|
||||
Counter::reset();
|
||||
return res;
|
||||
};
|
||||
|
||||
Indirect i1(e());
|
||||
VERIFY( i1.valueless_after_move() );
|
||||
verifyNoAllocations();
|
||||
|
||||
Indirect i2(std::allocator_arg, {}, e());
|
||||
VERIFY( i2.valueless_after_move() );
|
||||
verifyNoAllocations();
|
||||
|
||||
Indirect i3(val);
|
||||
i3 = e();
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 1 );
|
||||
|
||||
i3 = e();
|
||||
verifyNoAllocations();
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_all()
|
||||
{
|
||||
test_ctor();
|
||||
test_assign();
|
||||
test_swap();
|
||||
test_valueless();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_all();
|
||||
|
||||
static_assert([] {
|
||||
test_all();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
296
libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc
Normal file
296
libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc
Normal file
@@ -0,0 +1,296 @@
|
||||
// { dg-do run { target c++26 } }
|
||||
|
||||
#include <memory>
|
||||
#include <scoped_allocator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include <testsuite_hooks.h>
|
||||
#include <testsuite_allocator.h>
|
||||
|
||||
using __gnu_test::propagating_allocator;
|
||||
using __gnu_test::tracker_allocator;
|
||||
using Counter = __gnu_test::tracker_allocator_counter;
|
||||
|
||||
constexpr void
|
||||
verifyNoAllocations()
|
||||
{
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
}
|
||||
|
||||
template<bool Propagate>
|
||||
constexpr void
|
||||
test_ctor()
|
||||
{
|
||||
using PropAlloc = propagating_allocator<int, Propagate>;
|
||||
using Vector = std::vector<int, PropAlloc>;
|
||||
using ScopedAlloc = std::scoped_allocator_adaptor<
|
||||
propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
|
||||
PropAlloc>;
|
||||
using Indirect = std::indirect<Vector, ScopedAlloc>;
|
||||
|
||||
const Indirect val(std::in_place, {1, 2, 3});
|
||||
std::optional<Indirect> src;
|
||||
auto make = [&val, &src] -> Indirect&& {
|
||||
src.emplace(std::allocator_arg, ScopedAlloc{11, 22}, val);
|
||||
Counter::reset();
|
||||
return std::move(*src);
|
||||
};
|
||||
|
||||
Indirect i1(make());
|
||||
VERIFY( src->valueless_after_move() );
|
||||
VERIFY( *i1 == *val );
|
||||
VERIFY( i1->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i1.get_allocator().get_personality() == 11 );
|
||||
verifyNoAllocations();
|
||||
|
||||
Indirect i2(std::allocator_arg, ScopedAlloc{11, 22}, make());
|
||||
VERIFY( src->valueless_after_move() );
|
||||
VERIFY( *i2 == *val );
|
||||
VERIFY( i2->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i2.get_allocator().get_personality() == 11 );
|
||||
verifyNoAllocations();
|
||||
|
||||
Indirect i3(std::allocator_arg, ScopedAlloc{33, 44}, make());
|
||||
// We move-from contained object
|
||||
VERIFY( !src->valueless_after_move() );
|
||||
VERIFY( *i3 == *val );
|
||||
VERIFY( i3->get_allocator().get_personality() == 44 );
|
||||
VERIFY( i3.get_allocator().get_personality() == 33 );
|
||||
VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 1 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
}
|
||||
|
||||
template<bool Propagate>
|
||||
constexpr void
|
||||
test_assign()
|
||||
{
|
||||
using PropAlloc = propagating_allocator<int, Propagate>;
|
||||
using Vector = std::vector<int, PropAlloc>;
|
||||
using ScopedAlloc = std::scoped_allocator_adaptor<
|
||||
propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
|
||||
PropAlloc>;
|
||||
using Indirect = std::indirect<Vector, ScopedAlloc>;
|
||||
|
||||
const Indirect val(std::in_place, {1, 2, 3});
|
||||
std::optional<Indirect> src;
|
||||
auto make = [&val, &src] -> Indirect&& {
|
||||
src.emplace(std::allocator_arg, ScopedAlloc{11, 22}, val);
|
||||
Counter::reset();
|
||||
return std::move(*src);
|
||||
};
|
||||
|
||||
Indirect i1(std::allocator_arg, ScopedAlloc{11, 22});
|
||||
|
||||
i1 = make();
|
||||
VERIFY( src->valueless_after_move() );
|
||||
VERIFY( *i1 == *val );
|
||||
VERIFY( i1->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i1.get_allocator().get_personality() == 11 );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 1 );
|
||||
|
||||
Indirect i2(std::allocator_arg, ScopedAlloc{33, 44});
|
||||
|
||||
i2 = make();
|
||||
VERIFY( *i2 == *val );
|
||||
if (Propagate)
|
||||
{
|
||||
VERIFY( src->valueless_after_move() );
|
||||
VERIFY( i2->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i2.get_allocator().get_personality() == 11 );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// We allocate new holder and move-from contained object
|
||||
VERIFY( !src->valueless_after_move() );
|
||||
VERIFY( i2->get_allocator().get_personality() == 44 );
|
||||
VERIFY( i2.get_allocator().get_personality() == 33 );
|
||||
VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_construct_count() == 1 );
|
||||
}
|
||||
VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_destruct_count() == 1 );
|
||||
|
||||
Indirect i3(std::allocator_arg, ScopedAlloc{11, 22});
|
||||
auto(std::move(i3));
|
||||
|
||||
i3 = make();
|
||||
VERIFY( *i3 == *val );
|
||||
VERIFY( src->valueless_after_move() );
|
||||
VERIFY( i3->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i3.get_allocator().get_personality() == 11 );
|
||||
verifyNoAllocations();
|
||||
|
||||
Indirect i4(std::allocator_arg, ScopedAlloc{33, 44});
|
||||
auto(std::move(i4));
|
||||
|
||||
i4 = make();
|
||||
VERIFY( *i4 == *val );
|
||||
if (Propagate)
|
||||
{
|
||||
VERIFY( src->valueless_after_move() );
|
||||
VERIFY( i4->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i4.get_allocator().get_personality() == 11 );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// We allocate new holder and move-from contained object
|
||||
VERIFY( !src->valueless_after_move() );
|
||||
VERIFY( i4->get_allocator().get_personality() == 44 );
|
||||
VERIFY( i4.get_allocator().get_personality() == 33 );
|
||||
VERIFY( Counter::get_allocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_construct_count() == 1 );
|
||||
}
|
||||
VERIFY( Counter::get_deallocation_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 0 );
|
||||
}
|
||||
|
||||
template<bool Propagate>
|
||||
constexpr void
|
||||
test_swap()
|
||||
{
|
||||
using PropAlloc = propagating_allocator<int, Propagate>;
|
||||
using Vector = std::vector<int, PropAlloc>;
|
||||
using ScopedAlloc = std::scoped_allocator_adaptor<
|
||||
propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
|
||||
PropAlloc>;
|
||||
using Indirect = std::indirect<Vector, ScopedAlloc>;
|
||||
|
||||
const Indirect val1(std::in_place, {1, 2, 3});
|
||||
const Indirect val2(std::in_place, {2, 4, 6});
|
||||
|
||||
Indirect i1(std::allocator_arg, ScopedAlloc{11, 22}, val1);
|
||||
Indirect i2(std::allocator_arg, ScopedAlloc{11, 22}, val2);
|
||||
Counter::reset();
|
||||
i1.swap(i2);
|
||||
VERIFY( *i2 == *val1 );
|
||||
VERIFY( *i1 == *val2 );
|
||||
verifyNoAllocations();
|
||||
|
||||
auto(std::move(i1));
|
||||
|
||||
Counter::reset();
|
||||
i1.swap(i2);
|
||||
VERIFY( *i1 == *val1 );
|
||||
VERIFY( i2.valueless_after_move() );
|
||||
verifyNoAllocations();
|
||||
|
||||
if (!Propagate)
|
||||
return;
|
||||
|
||||
Indirect i3(std::allocator_arg, ScopedAlloc{33, 44}, val2);
|
||||
Counter::reset();
|
||||
i1.swap(i3);
|
||||
VERIFY( *i1 == *val2 );
|
||||
VERIFY( i1->get_allocator().get_personality() == 44 );
|
||||
VERIFY( i1.get_allocator().get_personality() == 33 );
|
||||
VERIFY( *i3 == *val1 );
|
||||
VERIFY( i3->get_allocator().get_personality() == 22 );
|
||||
VERIFY( i3.get_allocator().get_personality() == 11 );
|
||||
verifyNoAllocations();
|
||||
|
||||
i1.swap(i2);
|
||||
VERIFY( i1.valueless_after_move() );
|
||||
VERIFY( i1.get_allocator().get_personality() == 11 );
|
||||
VERIFY( *i2 == *val2 );
|
||||
VERIFY( i2->get_allocator().get_personality() == 44 );
|
||||
VERIFY( i2.get_allocator().get_personality() == 33 );
|
||||
verifyNoAllocations();
|
||||
}
|
||||
|
||||
template<bool Propagate>
|
||||
constexpr void
|
||||
test_valueless()
|
||||
{
|
||||
using PropAlloc = propagating_allocator<int, Propagate>;
|
||||
using Vector = std::vector<int, PropAlloc>;
|
||||
using ScopedAlloc = std::scoped_allocator_adaptor<
|
||||
propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>,
|
||||
PropAlloc>;
|
||||
using Indirect = std::indirect<Vector, ScopedAlloc>;
|
||||
|
||||
auto e = [] {
|
||||
Indirect res(std::allocator_arg, ScopedAlloc{11, 22});
|
||||
auto(std::move(res));
|
||||
Counter::reset();
|
||||
return res;
|
||||
};
|
||||
|
||||
Indirect i1(e());
|
||||
VERIFY( i1.valueless_after_move() );
|
||||
VERIFY( i1.get_allocator().get_personality() == 11 );
|
||||
verifyNoAllocations();
|
||||
|
||||
Indirect i2(std::allocator_arg, ScopedAlloc{33, 44}, e());
|
||||
VERIFY( i2.valueless_after_move() );
|
||||
VERIFY( i2.get_allocator().get_personality() == 33 );
|
||||
verifyNoAllocations();
|
||||
|
||||
Indirect i3(std::allocator_arg, ScopedAlloc{33, 44});
|
||||
|
||||
i3 = e();
|
||||
VERIFY( i3.valueless_after_move() );
|
||||
if (Propagate)
|
||||
VERIFY( i3.get_allocator().get_personality() == 11 );
|
||||
else
|
||||
VERIFY( i3.get_allocator().get_personality() == 33 );
|
||||
VERIFY( Counter::get_allocation_count() == 0 );
|
||||
VERIFY( Counter::get_deallocation_count() == sizeof(Vector) );
|
||||
VERIFY( Counter::get_construct_count() == 0 );
|
||||
VERIFY( Counter::get_destruct_count() == 1 );
|
||||
|
||||
i2 = e();
|
||||
VERIFY( i2.valueless_after_move() );
|
||||
if (Propagate)
|
||||
VERIFY( i2.get_allocator().get_personality() == 11 );
|
||||
else
|
||||
VERIFY( i2.get_allocator().get_personality() == 33 );
|
||||
verifyNoAllocations();
|
||||
|
||||
i3.swap(i2);
|
||||
VERIFY( i2.valueless_after_move() );
|
||||
VERIFY( i1.valueless_after_move() );
|
||||
verifyNoAllocations();
|
||||
|
||||
if (!Propagate)
|
||||
return;
|
||||
|
||||
Indirect i4(std::allocator_arg, ScopedAlloc{33, 44}, e());
|
||||
i4.swap(i1);
|
||||
verifyNoAllocations();
|
||||
}
|
||||
|
||||
template<bool Propagate>
|
||||
constexpr void
|
||||
test_all()
|
||||
{
|
||||
test_ctor<Propagate>();
|
||||
test_assign<Propagate>();
|
||||
test_swap<Propagate>();
|
||||
test_valueless<Propagate>();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_all<true>();
|
||||
test_all<false>();
|
||||
|
||||
static_assert([] {
|
||||
test_all<true>();
|
||||
test_all<false>();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
82
libstdc++-v3/testsuite/std/memory/indirect/relops.cc
Normal file
82
libstdc++-v3/testsuite/std/memory/indirect/relops.cc
Normal file
@@ -0,0 +1,82 @@
|
||||
// { dg-do run { target c++26 } }
|
||||
|
||||
#include <memory>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
struct Obj
|
||||
{
|
||||
int i;
|
||||
constexpr auto operator<=>(const Obj&) const = default;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::hash<Obj>
|
||||
{
|
||||
static size_t operator()(Obj const& obj)
|
||||
{ return std::hash<int>{}(obj.i); }
|
||||
};
|
||||
|
||||
constexpr void
|
||||
test_relops()
|
||||
{
|
||||
std::indirect<Obj> i1;
|
||||
VERIFY( i1 == i1 );
|
||||
VERIFY( i1 <= i1 );
|
||||
VERIFY( i1 >= i1 );
|
||||
|
||||
std::indirect<Obj> i2 = std::move(i1); // make i1 valueless
|
||||
VERIFY( i1 == i1 );
|
||||
VERIFY( i2 == i2 );
|
||||
VERIFY( i2 != i1 );
|
||||
VERIFY( i1 < i2 );
|
||||
VERIFY( i2 >= i1 );
|
||||
|
||||
std::indirect<Obj> i3 = std::move(i2); // make i2 valueless
|
||||
VERIFY( i2 == i1 );
|
||||
VERIFY( i2 >= i1 );
|
||||
VERIFY( i2 <= i1 );
|
||||
VERIFY( i3 > i2 );
|
||||
}
|
||||
|
||||
constexpr void
|
||||
test_comp_with_t()
|
||||
{
|
||||
std::indirect<Obj> i1;
|
||||
Obj o{2};
|
||||
VERIFY( i1 != o );
|
||||
VERIFY( i1 < o );
|
||||
|
||||
std::indirect<Obj> i2(Obj{2});
|
||||
VERIFY( i2 == o );
|
||||
VERIFY( i2 <= o );
|
||||
VERIFY( o <= i2 );
|
||||
|
||||
std::indirect<Obj> i3 = std::move(i2); // make i2 valueless
|
||||
VERIFY( i2 != o );
|
||||
VERIFY( i2 < o );
|
||||
}
|
||||
|
||||
void
|
||||
test_hash()
|
||||
{
|
||||
Obj o{5};
|
||||
std::indirect<Obj> i(o);
|
||||
VERIFY( std::hash<std::indirect<Obj>>{}(i)
|
||||
== std::hash<Obj>{}(o) );
|
||||
|
||||
auto(std::move(i)); // make i valueless
|
||||
(void)std::hash<std::indirect<Obj>>{}(i);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_relops();
|
||||
test_comp_with_t();
|
||||
test_hash();
|
||||
|
||||
static_assert([] {
|
||||
test_relops();
|
||||
test_comp_with_t();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user