mirror of
https://github.com/gcc-mirror/gcc.git
synced 2024-11-21 13:40:47 +00:00
libstdc++: Fix std::allocator_traits::construct constraints [PR108619]
Using std::is_constructible in the constraints introduces a spurious dependency on the type being destructible, which should not be required for constructing with an allocator. The test case shows a case where the type has a private destructor, which can be destroyed by the allocator, but std::is_destructible and std::is_constructible are false. Similarly, using is_nothrow_constructible in the noexcept-specifiers for the construct members of allocator_traits and std::allocator, __gnu_cxx::__new_allocator, and __gnu_cxx::__malloc_allocator gives the wrong answer if the type isn't destructible. We need a new type trait to define those correctly, so that we only check if the placement new-expression is nothrow after using is_constructible to check that it would be well-formed. Instead of just fixing the overly restrictive constraint to check for placement new, rewrite allocator_traits in terms of 'if constexpr' using variable templates and the detection idiom. Although we can use 'if constexpr' and variable templates in C++11 with appropriate uses of diagnostic pragmas, we can't have constexpr functions with multiple return statements. This means that in C++11 mode the _S_nothrow_construct and _S_nothrow_destroy helpers used for noexcept-specifiers still need to be overlaods using enable_if. Nearly everything else can be simplified to reduce overload resolution and enable_if checks. libstdc++-v3/ChangeLog: PR libstdc++/108619 * include/bits/alloc_traits.h (__allocator_traits_base): Add variable templates for detecting which allocator operations are supported. (allocator_traits): Use 'if constexpr' instead of dispatching to overloads constrained with enable_if. (allocator_traits<allocator<T>>::construct): Use Construct if construct_at is not supported. Use __is_nothrow_new_constructible for noexcept-specifier. (allocator_traits<allocator<void>>::construct): Use __is_nothrow_new_constructible for noexcept-specifier. * include/bits/new_allocator.h (construct): Likewise. * include/ext/malloc_allocator.h (construct): Likewise. * include/std/type_traits (__is_nothrow_new_constructible): New variable template. * testsuite/20_util/allocator/89510.cc: Adjust expected results. * testsuite/ext/malloc_allocator/89510.cc: Likewise. * testsuite/ext/new_allocator/89510.cc: Likewise. * testsuite/20_util/allocator_traits/members/108619.cc: New test.
This commit is contained in:
parent
43b8153c26
commit
8cf51d7516
@ -48,10 +48,19 @@ namespace std _GLIBCXX_VISIBILITY(default)
|
||||
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wc++14-extensions" // for variable templates
|
||||
#pragma GCC diagnostic ignored "-Wc++17-extensions" // for if-constexpr
|
||||
|
||||
/// @cond undocumented
|
||||
struct __allocator_traits_base
|
||||
{
|
||||
#if __cpp_concepts
|
||||
template<typename _Tp, typename _Up>
|
||||
#else
|
||||
template<typename _Tp, typename _Up, typename = void>
|
||||
#endif
|
||||
struct __rebind : __replace_first_arg<_Tp, _Up>
|
||||
{
|
||||
static_assert(is_same<
|
||||
@ -61,8 +70,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
};
|
||||
|
||||
template<typename _Tp, typename _Up>
|
||||
#if __cpp_concepts
|
||||
requires requires { typename _Tp::template rebind<_Up>::other; }
|
||||
struct __rebind<_Tp, _Up>
|
||||
#else
|
||||
struct __rebind<_Tp, _Up,
|
||||
__void_t<typename _Tp::template rebind<_Up>::other>>
|
||||
#endif
|
||||
{
|
||||
using type = typename _Tp::template rebind<_Up>::other;
|
||||
|
||||
@ -89,6 +103,135 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
using __pocs = typename _Tp::propagate_on_container_swap;
|
||||
template<typename _Tp>
|
||||
using __equal = __type_identity<typename _Tp::is_always_equal>;
|
||||
|
||||
// __has_allocate_hint is true if a.allocate(n, hint) is well-formed.
|
||||
#if __cpp_concepts
|
||||
template<typename _Alloc, typename _Sz, typename _Vp>
|
||||
static constexpr bool __has_allocate_hint
|
||||
= requires (_Alloc& __a, _Sz __n, _Vp __hint) {
|
||||
__a.allocate(__n, __hint);
|
||||
};
|
||||
#else
|
||||
template<typename _Alloc, typename _Sz, typename _Vp>
|
||||
using __allocate_hint_t
|
||||
= decltype(std::declval<_Alloc&>()
|
||||
.allocate(std::declval<_Sz>(), std::declval<_Vp>()));
|
||||
template<typename _Alloc, typename _Sz, typename _Vp, typename = void>
|
||||
static constexpr bool __has_allocate_hint = false;
|
||||
template<typename _Alloc, typename _Sz, typename _Vp>
|
||||
static constexpr bool
|
||||
__has_allocate_hint<_Alloc, _Sz, _Vp,
|
||||
__void_t<__allocate_hint_t<_Alloc, _Sz, _Vp>>>
|
||||
= true;
|
||||
#endif
|
||||
|
||||
// __has_construct is true if a.construct(p, args...) is well-formed.
|
||||
// __can_construct is true if either __has_construct is true, or if
|
||||
// a placement new-expression for T(args...) is well-formed. We use this
|
||||
// to constrain allocator_traits::construct, as a libstdc++ extension.
|
||||
#if __cpp_concepts
|
||||
template<typename _Alloc, typename _Tp, typename... _Args>
|
||||
static constexpr bool __has_construct
|
||||
= requires (_Alloc& __a, _Tp* __p, _Args&&... __args) {
|
||||
__a.construct(__p, std::forward<_Args>(__args)...);
|
||||
};
|
||||
template<typename _Tp, typename... _Args>
|
||||
static constexpr bool __can_construct_at
|
||||
= requires (_Tp* __p, _Args&&... __args) {
|
||||
#if __cpp_constexpr_dynamic_alloc
|
||||
std::construct_at(__p, std::forward<_Args>(__args)...);
|
||||
#else
|
||||
::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
|
||||
#endif
|
||||
};
|
||||
template<typename _Alloc, typename _Tp, typename... _Args>
|
||||
static constexpr bool __can_construct
|
||||
= __has_construct<_Alloc, _Tp, _Args...>
|
||||
|| __can_construct_at<_Tp, _Args...>;
|
||||
#else
|
||||
template<typename _Alloc, typename _Tp, typename... _Args>
|
||||
using __construct_t
|
||||
= decltype(std::declval<_Alloc&>().construct(std::declval<_Tp*>(),
|
||||
std::declval<_Args>()...));
|
||||
template<typename _Alloc, typename _Tp, typename, typename... _Args>
|
||||
static constexpr bool __has_construct_impl = false;
|
||||
template<typename _Alloc, typename _Tp, typename... _Args>
|
||||
static constexpr bool
|
||||
__has_construct_impl<_Alloc, _Tp,
|
||||
__void_t<__construct_t<_Alloc, _Tp, _Args...>>,
|
||||
_Args...>
|
||||
= true;
|
||||
template<typename _Alloc, typename _Tp, typename... _Args>
|
||||
static constexpr bool __has_construct
|
||||
= __has_construct_impl<_Alloc, _Tp, void, _Args...>;
|
||||
template<typename _Tp, typename... _Args>
|
||||
using __new_expr_t
|
||||
= decltype(::new((void*)0) _Tp(std::declval<_Args>()...));
|
||||
template<typename _Tp, typename, typename... _Args>
|
||||
static constexpr bool __has_new_expr = false;
|
||||
template<typename _Tp, typename... _Args>
|
||||
static constexpr bool
|
||||
__has_new_expr<_Tp, __void_t<__new_expr_t<_Tp, _Args...>>, _Args...>
|
||||
= true;
|
||||
template<typename _Alloc, typename _Tp, typename... _Args>
|
||||
static constexpr bool __can_construct
|
||||
= __has_construct<_Alloc, _Tp, _Args...>
|
||||
|| __has_new_expr<_Tp, void, _Args...>;
|
||||
#endif
|
||||
|
||||
// __has_destroy is true if a.destroy(p) is well-formed.
|
||||
#if __cpp_concepts
|
||||
template<typename _Alloc, typename _Tp>
|
||||
static constexpr bool __has_destroy = requires (_Alloc& __a, _Tp* __p) {
|
||||
__a.destroy(__p);
|
||||
};
|
||||
#else
|
||||
template<typename _Alloc, typename _Tp>
|
||||
using __destroy_t
|
||||
= decltype(std::declval<_Alloc&>().destroy(std::declval<_Tp*>()));
|
||||
template<typename _Alloc, typename _Tp, typename = void>
|
||||
static constexpr bool __has_destroy = false;
|
||||
template<typename _Alloc, typename _Tp>
|
||||
static constexpr bool __has_destroy<_Alloc, _Tp,
|
||||
__void_t<__destroy_t<_Alloc, _Tp>>>
|
||||
= true;
|
||||
#endif
|
||||
|
||||
// __has_max_size is true if a.max_size() is well-formed.
|
||||
#if __cpp_concepts
|
||||
template<typename _Alloc>
|
||||
static constexpr bool __has_max_size = requires (const _Alloc& __a) {
|
||||
__a.max_size();
|
||||
};
|
||||
#else
|
||||
template<typename _Alloc>
|
||||
using __max_size_t = decltype(std::declval<const _Alloc&>().max_size());
|
||||
template<typename _Alloc, typename = void>
|
||||
static constexpr bool __has_max_size = false;
|
||||
template<typename _Alloc>
|
||||
static constexpr bool __has_max_size<_Alloc,
|
||||
__void_t<__max_size_t<_Alloc>>>
|
||||
= true;
|
||||
#endif
|
||||
|
||||
// __has_soccc is true if a.select_on_container_copy_construction()
|
||||
// is well-formed.
|
||||
#if __cpp_concepts
|
||||
template<typename _Alloc>
|
||||
static constexpr bool __has_soccc = requires (const _Alloc& __a) {
|
||||
__a.select_on_container_copy_construction();
|
||||
};
|
||||
#else
|
||||
template<typename _Alloc>
|
||||
using __soccc_t
|
||||
= decltype(std::declval<const _Alloc&>()
|
||||
.select_on_container_copy_construction());
|
||||
template<typename _Alloc, typename = void>
|
||||
static constexpr bool __has_soccc = false;
|
||||
template<typename _Alloc>
|
||||
static constexpr bool __has_soccc<_Alloc, __void_t<__soccc_t<_Alloc>>>
|
||||
= true;
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename _Alloc, typename _Up>
|
||||
@ -230,98 +373,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
template<typename _Tp>
|
||||
using rebind_traits = allocator_traits<rebind_alloc<_Tp>>;
|
||||
|
||||
private:
|
||||
template<typename _Alloc2>
|
||||
static constexpr auto
|
||||
_S_allocate(_Alloc2& __a, size_type __n, const_void_pointer __hint, int)
|
||||
-> decltype(__a.allocate(__n, __hint))
|
||||
{ return __a.allocate(__n, __hint); }
|
||||
|
||||
template<typename _Alloc2>
|
||||
static constexpr pointer
|
||||
_S_allocate(_Alloc2& __a, size_type __n, const_void_pointer, ...)
|
||||
{ return __a.allocate(__n); }
|
||||
|
||||
template<typename _Tp, typename... _Args>
|
||||
struct __construct_helper
|
||||
{
|
||||
template<typename _Alloc2,
|
||||
typename = decltype(std::declval<_Alloc2*>()->construct(
|
||||
std::declval<_Tp*>(), std::declval<_Args>()...))>
|
||||
static true_type __test(int);
|
||||
|
||||
template<typename>
|
||||
static false_type __test(...);
|
||||
|
||||
using type = decltype(__test<_Alloc>(0));
|
||||
};
|
||||
|
||||
template<typename _Tp, typename... _Args>
|
||||
using __has_construct
|
||||
= typename __construct_helper<_Tp, _Args...>::type;
|
||||
|
||||
template<typename _Tp, typename... _Args>
|
||||
static _GLIBCXX14_CONSTEXPR _Require<__has_construct<_Tp, _Args...>>
|
||||
_S_construct(_Alloc& __a, _Tp* __p, _Args&&... __args)
|
||||
noexcept(noexcept(__a.construct(__p, std::forward<_Args>(__args)...)))
|
||||
{ __a.construct(__p, std::forward<_Args>(__args)...); }
|
||||
|
||||
template<typename _Tp, typename... _Args>
|
||||
static _GLIBCXX14_CONSTEXPR
|
||||
_Require<__and_<__not_<__has_construct<_Tp, _Args...>>,
|
||||
is_constructible<_Tp, _Args...>>>
|
||||
_S_construct(_Alloc&, _Tp* __p, _Args&&... __args)
|
||||
noexcept(std::is_nothrow_constructible<_Tp, _Args...>::value)
|
||||
{
|
||||
#if __cplusplus <= 201703L
|
||||
::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
|
||||
#else
|
||||
std::construct_at(__p, std::forward<_Args>(__args)...);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename _Alloc2, typename _Tp>
|
||||
static _GLIBCXX14_CONSTEXPR auto
|
||||
_S_destroy(_Alloc2& __a, _Tp* __p, int)
|
||||
noexcept(noexcept(__a.destroy(__p)))
|
||||
-> decltype(__a.destroy(__p))
|
||||
{ __a.destroy(__p); }
|
||||
|
||||
template<typename _Alloc2, typename _Tp>
|
||||
static _GLIBCXX14_CONSTEXPR void
|
||||
_S_destroy(_Alloc2&, _Tp* __p, ...)
|
||||
noexcept(std::is_nothrow_destructible<_Tp>::value)
|
||||
{ std::_Destroy(__p); }
|
||||
|
||||
template<typename _Alloc2>
|
||||
static constexpr auto
|
||||
_S_max_size(_Alloc2& __a, int)
|
||||
-> decltype(__a.max_size())
|
||||
{ return __a.max_size(); }
|
||||
|
||||
template<typename _Alloc2>
|
||||
static constexpr size_type
|
||||
_S_max_size(_Alloc2&, ...)
|
||||
{
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// 2466. allocator_traits::max_size() default behavior is incorrect
|
||||
return __gnu_cxx::__numeric_traits<size_type>::__max
|
||||
/ sizeof(value_type);
|
||||
}
|
||||
|
||||
template<typename _Alloc2>
|
||||
static constexpr auto
|
||||
_S_select(_Alloc2& __a, int)
|
||||
-> decltype(__a.select_on_container_copy_construction())
|
||||
{ return __a.select_on_container_copy_construction(); }
|
||||
|
||||
template<typename _Alloc2>
|
||||
static constexpr _Alloc2
|
||||
_S_select(_Alloc2& __a, ...)
|
||||
{ return __a; }
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Allocate memory.
|
||||
* @param __a An allocator.
|
||||
@ -346,7 +397,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
*/
|
||||
_GLIBCXX_NODISCARD static _GLIBCXX20_CONSTEXPR pointer
|
||||
allocate(_Alloc& __a, size_type __n, const_void_pointer __hint)
|
||||
{ return _S_allocate(__a, __n, __hint, 0); }
|
||||
{
|
||||
if constexpr (__has_allocate_hint<_Alloc, size_type, const_void_pointer>)
|
||||
return __a.allocate(__n, __hint);
|
||||
else
|
||||
return __a.allocate(__n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deallocate memory.
|
||||
@ -372,12 +428,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
* arguments @a __args...
|
||||
*/
|
||||
template<typename _Tp, typename... _Args>
|
||||
static _GLIBCXX20_CONSTEXPR auto
|
||||
#if __cpp_concepts && __cpp_constexpr_dynamic_alloc
|
||||
requires __can_construct<_Alloc, _Tp, _Args...>
|
||||
static constexpr void
|
||||
#else
|
||||
static __enable_if_t<__can_construct<_Alloc, _Tp, _Args...>>
|
||||
#endif
|
||||
construct(_Alloc& __a, _Tp* __p, _Args&&... __args)
|
||||
noexcept(noexcept(_S_construct(__a, __p,
|
||||
std::forward<_Args>(__args)...)))
|
||||
-> decltype(_S_construct(__a, __p, std::forward<_Args>(__args)...))
|
||||
{ _S_construct(__a, __p, std::forward<_Args>(__args)...); }
|
||||
noexcept(_S_nothrow_construct<_Tp, _Args...>())
|
||||
{
|
||||
if constexpr (__has_construct<_Alloc, _Tp, _Args...>)
|
||||
__a.construct(__p, std::forward<_Args>(__args)...);
|
||||
else
|
||||
std::_Construct(__p, std::forward<_Args>(__args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroy an object of type @a _Tp
|
||||
@ -390,8 +454,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
template<typename _Tp>
|
||||
static _GLIBCXX20_CONSTEXPR void
|
||||
destroy(_Alloc& __a, _Tp* __p)
|
||||
noexcept(noexcept(_S_destroy(__a, __p, 0)))
|
||||
{ _S_destroy(__a, __p, 0); }
|
||||
noexcept(_S_nothrow_destroy<_Tp>())
|
||||
{
|
||||
if constexpr (__has_destroy<_Alloc, _Tp>)
|
||||
__a.destroy(__p);
|
||||
else
|
||||
std::_Destroy(__p);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The maximum supported allocation size
|
||||
@ -403,7 +472,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
*/
|
||||
static _GLIBCXX20_CONSTEXPR size_type
|
||||
max_size(const _Alloc& __a) noexcept
|
||||
{ return _S_max_size(__a, 0); }
|
||||
{
|
||||
if constexpr (__has_max_size<_Alloc>)
|
||||
return __a.max_size();
|
||||
else
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// 2466. allocator_traits::max_size() default behavior is incorrect
|
||||
return __gnu_cxx::__numeric_traits<size_type>::__max
|
||||
/ sizeof(value_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtain an allocator to use when copying a container.
|
||||
@ -415,8 +492,61 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
*/
|
||||
static _GLIBCXX20_CONSTEXPR _Alloc
|
||||
select_on_container_copy_construction(const _Alloc& __rhs)
|
||||
{ return _S_select(__rhs, 0); }
|
||||
{
|
||||
if constexpr (__has_soccc<_Alloc>)
|
||||
return __rhs.select_on_container_copy_construction();
|
||||
else
|
||||
return __rhs;
|
||||
}
|
||||
|
||||
private:
|
||||
#if __cpp_constexpr >= 201304 // >= C++14
|
||||
template<typename _Tp, typename... _Args>
|
||||
static constexpr bool
|
||||
_S_nothrow_construct(_Alloc* __a = nullptr, _Tp* __p = nullptr)
|
||||
{
|
||||
if constexpr (__has_construct<_Alloc, _Tp, _Args...>)
|
||||
return noexcept(__a->construct(__p, std::declval<_Args>()...));
|
||||
else
|
||||
return __is_nothrow_new_constructible<_Tp, _Args...>;
|
||||
}
|
||||
|
||||
template<typename _Tp>
|
||||
static constexpr bool
|
||||
_S_nothrow_destroy(_Alloc* __a = nullptr, _Tp* __p = nullptr)
|
||||
{
|
||||
if constexpr (__has_destroy<_Alloc, _Tp>)
|
||||
return noexcept(__a->destroy(__p));
|
||||
else
|
||||
return is_nothrow_destructible<_Tp>::value;
|
||||
}
|
||||
#else
|
||||
template<typename _Tp, typename... _Args>
|
||||
static constexpr
|
||||
__enable_if_t<__has_construct<_Alloc, _Tp, _Args...>, bool>
|
||||
_S_nothrow_construct(_Alloc* __a = nullptr, _Tp* __p = nullptr)
|
||||
{ return noexcept(__a->construct(__p, std::declval<_Args>()...)); }
|
||||
|
||||
template<typename _Tp, typename... _Args>
|
||||
static constexpr
|
||||
__enable_if_t<!__has_construct<_Alloc, _Tp, _Args...>, bool>
|
||||
_S_nothrow_construct(_Alloc* = nullptr, _Tp* __p = nullptr)
|
||||
{ return __is_nothrow_new_constructible<_Tp, _Args...>; }
|
||||
|
||||
template<typename _Tp>
|
||||
static constexpr
|
||||
__enable_if_t<__has_destroy<_Alloc, _Tp>, bool>
|
||||
_S_nothrow_destroy(_Alloc* __a = nullptr, _Tp* __p = nullptr)
|
||||
{ return noexcept(__a->destroy(__p)); }
|
||||
|
||||
template<typename _Tp>
|
||||
static constexpr
|
||||
__enable_if_t<!__has_destroy<_Alloc, _Tp>, bool>
|
||||
_S_nothrow_destroy(_Alloc* = nullptr, _Tp* __p = nullptr)
|
||||
{ return is_nothrow_destructible<_Tp>::value; }
|
||||
#endif
|
||||
};
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#if _GLIBCXX_HOSTED
|
||||
/// Partial specialization for std::allocator.
|
||||
@ -526,14 +656,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
template<typename _Up, typename... _Args>
|
||||
[[__gnu__::__always_inline__]]
|
||||
static _GLIBCXX20_CONSTEXPR void
|
||||
construct(allocator_type& __a __attribute__((__unused__)), _Up* __p,
|
||||
_Args&&... __args)
|
||||
noexcept(std::is_nothrow_constructible<_Up, _Args...>::value)
|
||||
construct(allocator_type& __a __attribute__((__unused__)),
|
||||
_Up* __p, _Args&&... __args)
|
||||
#if __cplusplus <= 201703L
|
||||
noexcept(noexcept(__a.construct(__p, std::forward<_Args>(__args)...)))
|
||||
#else
|
||||
noexcept(__is_nothrow_new_constructible<_Up, _Args...>)
|
||||
#endif
|
||||
{
|
||||
#if __cplusplus <= 201703L
|
||||
__a.construct(__p, std::forward<_Args>(__args)...);
|
||||
#else
|
||||
#elif __cpp_constexpr_dynamic_alloc // >= C++20
|
||||
std::construct_at(__p, std::forward<_Args>(__args)...);
|
||||
#else
|
||||
std::_Construct(__p, std::forward<_Args>(__args)...);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -653,7 +789,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
[[__gnu__::__always_inline__]]
|
||||
static _GLIBCXX20_CONSTEXPR void
|
||||
construct(allocator_type&, _Up* __p, _Args&&... __args)
|
||||
noexcept(std::is_nothrow_constructible<_Up, _Args...>::value)
|
||||
noexcept(__is_nothrow_new_constructible<_Up, _Args...>)
|
||||
{ std::_Construct(__p, std::forward<_Args>(__args)...); }
|
||||
|
||||
/**
|
||||
@ -944,6 +1080,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
std::_Destroy(__first, __last);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @endcond
|
||||
|
||||
_GLIBCXX_END_NAMESPACE_VERSION
|
||||
|
@ -187,7 +187,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
__attribute__((__always_inline__))
|
||||
void
|
||||
construct(_Up* __p, _Args&&... __args)
|
||||
noexcept(std::is_nothrow_constructible<_Up, _Args...>::value)
|
||||
noexcept(__is_nothrow_new_constructible<_Up, _Args...>)
|
||||
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
|
||||
|
||||
template<typename _Up>
|
||||
|
@ -161,7 +161,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
template<typename _Up, typename... _Args>
|
||||
void
|
||||
construct(_Up* __p, _Args&&... __args)
|
||||
noexcept(std::is_nothrow_constructible<_Up, _Args...>::value)
|
||||
noexcept(std::__is_nothrow_new_constructible<_Up, _Args...>)
|
||||
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
|
||||
|
||||
template<typename _Up>
|
||||
|
@ -1643,6 +1643,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
#endif
|
||||
#endif // __cpp_lib_is_nothrow_convertible
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wc++14-extensions" // for variable templates
|
||||
template<typename _Tp, typename... _Args>
|
||||
struct __is_nothrow_new_constructible_impl
|
||||
: __bool_constant<
|
||||
noexcept(::new(std::declval<void*>()) _Tp(std::declval<_Args>()...))
|
||||
>
|
||||
{ };
|
||||
|
||||
template<typename _Tp, typename... _Args>
|
||||
_GLIBCXX17_INLINE constexpr bool __is_nothrow_new_constructible
|
||||
= __and_<is_constructible<_Tp, _Args...>,
|
||||
__is_nothrow_new_constructible_impl<_Tp, _Args...>>::value;
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// Const-volatile modifications.
|
||||
|
||||
/// remove_const
|
||||
|
@ -136,13 +136,11 @@ struct Z
|
||||
};
|
||||
|
||||
Z* zp;
|
||||
// These construct calls should be noexcept, but they are false because
|
||||
// they use is_nothrow_constructible which depends on is_nothrow_destructible.
|
||||
#if __cplusplus <= 201703L
|
||||
static_assert( ! noexcept(a.construct(zp)), "wrong" );
|
||||
static_assert( ! noexcept(a.construct(zp, 1)), "wrong" );
|
||||
static_assert( ! noexcept(a.destroy(zp)), "" );
|
||||
static_assert( noexcept(a.construct(zp)), "" );
|
||||
static_assert( noexcept(a.construct(zp, 1)), "" );
|
||||
static_assert( ! noexcept(a.destroy(zp)), "~Z is noexcept(false)" );
|
||||
#endif
|
||||
static_assert( ! noexcept(AT::construct(a, zp)), "" );
|
||||
static_assert( ! noexcept(AT::construct(a, zp, 1)), "" );
|
||||
static_assert( ! noexcept(AT::destroy(a, zp)), "" );
|
||||
static_assert( noexcept(AT::construct(a, zp)), "" );
|
||||
static_assert( noexcept(AT::construct(a, zp, 1)), "" );
|
||||
static_assert( ! noexcept(AT::destroy(a, zp)), "~Z is noexcept(false)" );
|
||||
|
@ -0,0 +1,35 @@
|
||||
// { dg-do compile { target c++11 } }
|
||||
|
||||
#include <memory>
|
||||
|
||||
template<typename T>
|
||||
struct Alloc
|
||||
{
|
||||
Alloc() = default;
|
||||
|
||||
template<typename U> Alloc(const Alloc<U>&) { }
|
||||
|
||||
using value_type = T;
|
||||
|
||||
T* allocate(unsigned n)
|
||||
{ return std::allocator<T>().allocate(n); }
|
||||
|
||||
void deallocate(T* p, unsigned n)
|
||||
{ return std::allocator<T>().deallocate(p, n); }
|
||||
|
||||
template<typename U> void destroy(U* p){ p->~U(); }
|
||||
};
|
||||
|
||||
|
||||
class S
|
||||
{
|
||||
~S() = default;
|
||||
|
||||
friend Alloc<S>;
|
||||
};
|
||||
|
||||
void
|
||||
test_pr108619(Alloc<int> a, S* p)
|
||||
{
|
||||
std::allocator_traits<Alloc<int>>::construct(a, p);
|
||||
}
|
@ -137,13 +137,11 @@ struct Z
|
||||
};
|
||||
|
||||
Z* zp;
|
||||
// These construct calls should be noexcept, but they are false because
|
||||
// they use is_nothrow_constructible which depends on is_nothrow_destructible.
|
||||
#if __cplusplus <= 201703L
|
||||
static_assert( ! noexcept(a.construct(zp)), "wrong" );
|
||||
static_assert( ! noexcept(a.construct(zp, 1)), "wrong" );
|
||||
static_assert( ! noexcept(a.destroy(zp)), "" );
|
||||
static_assert( noexcept(a.construct(zp)), "" );
|
||||
static_assert( noexcept(a.construct(zp, 1)), "" );
|
||||
static_assert( ! noexcept(a.destroy(zp)), "~Z is noexcept(false)" );
|
||||
#endif
|
||||
static_assert( ! noexcept(AT::construct(a, zp)), "" );
|
||||
static_assert( ! noexcept(AT::construct(a, zp, 1)), "" );
|
||||
static_assert( ! noexcept(AT::destroy(a, zp)), "" );
|
||||
static_assert( noexcept(AT::construct(a, zp)), "" );
|
||||
static_assert( noexcept(AT::construct(a, zp, 1)), "" );
|
||||
static_assert( ! noexcept(AT::destroy(a, zp)), "~Z is noexcept(false)" );
|
||||
|
@ -137,13 +137,11 @@ struct Z
|
||||
};
|
||||
|
||||
Z* zp;
|
||||
// These construct calls should be noexcept, but they are false because
|
||||
// they use is_nothrow_constructible which depends on is_nothrow_destructible.
|
||||
#if __cplusplus <= 201703L
|
||||
static_assert( ! noexcept(a.construct(zp)), "wrong" );
|
||||
static_assert( ! noexcept(a.construct(zp, 1)), "wrong" );
|
||||
static_assert( ! noexcept(a.destroy(zp)), "" );
|
||||
static_assert( noexcept(a.construct(zp)), "" );
|
||||
static_assert( noexcept(a.construct(zp, 1)), "" );
|
||||
static_assert( ! noexcept(a.destroy(zp)), "~Z is noexcept(false)" );
|
||||
#endif
|
||||
static_assert( ! noexcept(AT::construct(a, zp)), "" );
|
||||
static_assert( ! noexcept(AT::construct(a, zp, 1)), "" );
|
||||
static_assert( ! noexcept(AT::destroy(a, zp)), "" );
|
||||
static_assert( noexcept(AT::construct(a, zp)), "" );
|
||||
static_assert( noexcept(AT::construct(a, zp, 1)), "" );
|
||||
static_assert( ! noexcept(AT::destroy(a, zp)), "~Z is noexcept(false)" );
|
||||
|
Loading…
Reference in New Issue
Block a user