libstdc++: Fix missing members in std::allocator<void>
The changes in 75c6a925da
were slightly
incorrect, because the converting constructor should be noexcept, and
the POCMA and is_always_equal traits should still be present in C++20.
This fixes it, and slightly refactors the preprocessor conditions and
order of members. Also add comments explaining things.
The non-standard construct and destroy members added for PR 78052 can be
private if allocator_traits<allocator<void>> is made a friend.
libstdc++-v3/ChangeLog:
* include/bits/allocator.h (allocator<void>) [C++20]: Add
missing noexcept to constructor. Restore missing POCMA and
is_always_equal_traits.
[C++17]: Make construct and destroy members private and
declare allocator_traits as a friend.
* include/bits/memoryfwd.h (allocator_traits): Declare.
* include/ext/malloc_allocator.h (malloc_allocator::allocate):
Add nodiscard attribute. Add static assertion for LWG 3307.
* include/ext/new_allocator.h (new_allocator::allocate): Add
static assertion for LWG 3307.
* testsuite/20_util/allocator/void.cc: Check that converting
constructor is noexcept. Check for propagation traits and
size_type and difference_type. Check that pointer and
const_pointer are gone in C++20.
This commit is contained in:
parent
5ea40269a7
commit
5e3a1ea3d8
|
@ -60,6 +60,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
* @{
|
||||
*/
|
||||
|
||||
// Since C++20 the primary template should be used for allocator<void>,
|
||||
// but then it would have a non-trivial default ctor and dtor, which
|
||||
// would be an ABI change. So C++20 still uses the allocator<void> explicit
|
||||
// specialization, with the historical ABI properties, but with the same
|
||||
// members that are present in the primary template.
|
||||
|
||||
#if ! _GLIBCXX_INLINE_VERSION
|
||||
/// allocator<void> specialization.
|
||||
template<>
|
||||
class allocator<void>
|
||||
|
@ -68,28 +75,40 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
typedef void value_type;
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
|
||||
#if __cplusplus <= 201703L
|
||||
// These were removed for C++20.
|
||||
typedef void* pointer;
|
||||
typedef const void* const_pointer;
|
||||
|
||||
template<typename _Tp1>
|
||||
struct rebind
|
||||
{ typedef allocator<_Tp1> other; };
|
||||
#else
|
||||
allocator() = default;
|
||||
#endif
|
||||
|
||||
template<typename _Up>
|
||||
constexpr
|
||||
allocator(const allocator<_Up>&) { }
|
||||
#endif // ! C++20
|
||||
|
||||
#if __cplusplus >= 201103L && __cplusplus <= 201703L
|
||||
#if __cplusplus >= 201103L
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// 2103. std::allocator propagate_on_container_move_assignment
|
||||
typedef true_type propagate_on_container_move_assignment;
|
||||
|
||||
typedef true_type is_always_equal;
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
allocator() = default;
|
||||
|
||||
template<typename _Up>
|
||||
constexpr
|
||||
allocator(const allocator<_Up>&) noexcept { }
|
||||
|
||||
// No allocate member because it's ill-formed by LWG 3307.
|
||||
// No deallocate member because it would be undefined to call it
|
||||
// with any pointer which wasn't obtained from allocate.
|
||||
|
||||
#else // ! C++20
|
||||
private:
|
||||
// This uses construct and destroy in C++11/14/17 modes.
|
||||
friend allocator_traits<allocator<void>>;
|
||||
|
||||
template<typename _Up, typename... _Args>
|
||||
void
|
||||
construct(_Up* __p, _Args&&... __args)
|
||||
|
@ -101,11 +120,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
destroy(_Up* __p)
|
||||
noexcept(std::is_nothrow_destructible<_Up>::value)
|
||||
{ __p->~_Up(); }
|
||||
#endif // C++11 to C++17
|
||||
#endif // C++17
|
||||
#endif // C++11
|
||||
|
||||
};
|
||||
#endif // ! _GLIBCXX_INLINE_VERSION
|
||||
|
||||
/**
|
||||
* @brief The @a standard allocator, as per [20.4].
|
||||
* @brief The @a standard allocator, as per C++03 [20.4.1].
|
||||
*
|
||||
* See https://gcc.gnu.org/onlinedocs/libstdc++/manual/memory.html#std.util.memory.allocator
|
||||
* for further details.
|
||||
|
@ -119,7 +141,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
typedef _Tp value_type;
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
|
||||
#if __cplusplus <= 201703L
|
||||
// These were removed for C++20.
|
||||
typedef _Tp* pointer;
|
||||
typedef const _Tp* const_pointer;
|
||||
typedef _Tp& reference;
|
||||
|
|
|
@ -63,15 +63,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
template<typename>
|
||||
class allocator;
|
||||
|
||||
#if __cplusplus <= 201703L
|
||||
template<>
|
||||
class allocator<void>;
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
/// Declare uses_allocator so it can be specialized in \<queue\> etc.
|
||||
/// Declare uses_allocator so it can be specialized in `<queue>` etc.
|
||||
template<typename, typename>
|
||||
struct uses_allocator;
|
||||
|
||||
template<typename>
|
||||
struct allocator_traits;
|
||||
#endif
|
||||
|
||||
/// @} group memory
|
||||
|
|
|
@ -99,9 +99,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
|
||||
// NB: __n is permitted to be 0. The C++ standard says nothing
|
||||
// about what the return value is when __n == 0.
|
||||
_Tp*
|
||||
_GLIBCXX_NODISCARD _Tp*
|
||||
allocate(size_type __n, const void* = 0)
|
||||
{
|
||||
#if __cplusplus >= 201103L
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// 3308. std::allocator<void>().allocate(n)
|
||||
static_assert(sizeof(_Tp) != 0, "cannot allocate incomplete types");
|
||||
#endif
|
||||
|
||||
if (__builtin_expect(__n > this->_M_max_size(), false))
|
||||
{
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
|
|||
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
|
||||
/**
|
||||
* @brief An allocator that uses global new, as per [20.4].
|
||||
* @brief An allocator that uses global new, as per C++03 [20.4.1].
|
||||
* @ingroup allocators
|
||||
*
|
||||
* This is precisely the allocator defined in the C++ Standard.
|
||||
|
@ -102,6 +102,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
_GLIBCXX_NODISCARD _Tp*
|
||||
allocate(size_type __n, const void* = static_cast<const void*>(0))
|
||||
{
|
||||
#if __cplusplus >= 201103L
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// 3308. std::allocator<void>().allocate(n)
|
||||
static_assert(sizeof(_Tp) != 0, "cannot allocate incomplete types");
|
||||
#endif
|
||||
|
||||
if (__builtin_expect(__n > this->_M_max_size(), false))
|
||||
{
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
|
|
|
@ -33,6 +33,18 @@ test01()
|
|||
std::allocator_traits<alloc_type>::destroy(a, &i);
|
||||
}
|
||||
|
||||
static_assert( std::allocator<void>::propagate_on_container_move_assignment(),
|
||||
"POCMA trait should always be present" );
|
||||
static_assert( std::allocator<void>::is_always_equal(),
|
||||
"is_always_equal trait should always be present" );
|
||||
|
||||
static_assert(
|
||||
std::is_same<std::allocator<void>::size_type, std::size_t>(),
|
||||
"size_type is size_t" );
|
||||
static_assert(
|
||||
std::is_same<std::allocator<void>::difference_type, std::ptrdiff_t>(),
|
||||
"size_type is size_t" );
|
||||
|
||||
// These properties are formally unspecified, but have always been true for
|
||||
// the libstdc++ definition of allocator<void>.
|
||||
static_assert(
|
||||
|
@ -48,12 +60,32 @@ static_assert(
|
|||
std::is_trivially_destructible<std::allocator<void>>::value,
|
||||
"explicit specialization has trivial destructor");
|
||||
|
||||
#if __cplusplus > 201703L
|
||||
#if __cplusplus >= 202002L
|
||||
// C++20 removes the allocator<void> explicit specialization, so it can now be
|
||||
// constructed using the converting constructor from other specializations.
|
||||
static_assert( std::is_constructible_v<std::allocator<void>,
|
||||
static_assert( std::is_nothrow_constructible_v<std::allocator<void>,
|
||||
std::allocator<int>> );
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
concept has_pointer = requires { typename T::pointer; };
|
||||
template<typename T>
|
||||
concept has_const_pointer = requires { typename T::const_pointer; };
|
||||
template<typename T>
|
||||
concept has_size_type = requires { typename T::size_type; };
|
||||
template<typename T>
|
||||
concept has_difference_type = requires { typename T::difference_type; };
|
||||
|
||||
// These were removed for C++20
|
||||
static_assert( ! has_pointer<std::allocator<void>> );
|
||||
static_assert( ! has_const_pointer<std::allocator<void>> );
|
||||
|
||||
#else
|
||||
static_assert(
|
||||
std::is_same<std::allocator<void>::pointer, void*>(),
|
||||
"pointer is void*" );
|
||||
static_assert( std::is_same<std::allocator<void>::const_pointer, const void*>(),
|
||||
"const_pointer is const void*" );
|
||||
#endif // C++20
|
||||
|
||||
int
|
||||
main()
|
||||
|
|
Loading…
Reference in New Issue