Skip to content

Commit f93a11b

Browse files
[libc++] Partially implement LWG3187 for polymorphic_allocator
Applying it (adding missing `const`) to pre-C++20 dispatching mechanism. - Also implement LWG3677 for `tuple`, by modifications to `__memory/allocator_arg_t.h`. - Also partially implement LWG4312 for `polymorphic_allocator`, by reusing relevant C++20 mechanism changes around `allocator_arg_t`. - Also add fallback dispatching and overloads to emit better error messages.
1 parent 8c8196c commit f93a11b

File tree

5 files changed

+57
-72
lines changed

5 files changed

+57
-72
lines changed

libcxx/include/__memory/allocator_arg_t.h

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <__memory/uses_allocator.h>
1515
#include <__type_traits/integral_constant.h>
1616
#include <__type_traits/is_constructible.h>
17+
#include <__type_traits/remove_cv.h>
1718
#include <__type_traits/remove_cvref.h>
1819
#include <__utility/forward.h>
1920

@@ -40,34 +41,15 @@ constexpr allocator_arg_t allocator_arg = allocator_arg_t();
4041
template <class _Tp, class _Alloc, class... _Args>
4142
struct __uses_alloc_ctor_imp {
4243
using _RawAlloc _LIBCPP_NODEBUG = __remove_cvref_t<_Alloc>;
43-
static const bool __ua = uses_allocator<_Tp, _RawAlloc>::value;
44-
static const bool __ic = is_constructible<_Tp, allocator_arg_t, _Alloc, _Args...>::value;
45-
static const int value = __ua ? 2 - __ic : 0;
44+
static constexpr bool __ua = uses_allocator<__remove_cv_t<_Tp>, _RawAlloc>::value;
45+
static constexpr bool __ic_head = is_constructible<_Tp, allocator_arg_t, const _RawAlloc&, _Args...>::value;
46+
static constexpr bool __ic_tail = is_constructible<_Tp, _Args..., const _RawAlloc&>::value;
47+
static constexpr int value = __ua ? (__ic_head ? 1 : __ic_tail ? 2 : -1) : 0;
4648
};
4749

4850
template <class _Tp, class _Alloc, class... _Args>
4951
struct __uses_alloc_ctor : integral_constant<int, __uses_alloc_ctor_imp<_Tp, _Alloc, _Args...>::value> {};
5052

51-
template <class _Tp, class _Allocator, class... _Args>
52-
inline _LIBCPP_HIDE_FROM_ABI void
53-
__user_alloc_construct_impl(integral_constant<int, 0>, _Tp* __storage, const _Allocator&, _Args&&... __args) {
54-
new (__storage) _Tp(std::forward<_Args>(__args)...);
55-
}
56-
57-
// FIXME: This should have a version which takes a non-const alloc.
58-
template <class _Tp, class _Allocator, class... _Args>
59-
inline _LIBCPP_HIDE_FROM_ABI void
60-
__user_alloc_construct_impl(integral_constant<int, 1>, _Tp* __storage, const _Allocator& __a, _Args&&... __args) {
61-
new (__storage) _Tp(allocator_arg, __a, std::forward<_Args>(__args)...);
62-
}
63-
64-
// FIXME: This should have a version which takes a non-const alloc.
65-
template <class _Tp, class _Allocator, class... _Args>
66-
inline _LIBCPP_HIDE_FROM_ABI void
67-
__user_alloc_construct_impl(integral_constant<int, 2>, _Tp* __storage, const _Allocator& __a, _Args&&... __args) {
68-
new (__storage) _Tp(std::forward<_Args>(__args)..., __a);
69-
}
70-
7153
#endif // _LIBCPP_CXX03_LANG
7254

7355
_LIBCPP_END_NAMESPACE_STD

libcxx/include/__memory/uses_allocator_construction.h

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
#define _LIBCPP___MEMORY_USES_ALLOCATOR_CONSTRUCTION_H
1111

1212
#include <__config>
13+
#include <__functional/reference_wrapper.h>
1314
#include <__memory/construct_at.h>
1415
#include <__memory/uses_allocator.h>
1516
#include <__tuple/tuple_like_no_subrange.h>
1617
#include <__type_traits/enable_if.h>
18+
#include <__type_traits/is_constructible.h>
1719
#include <__type_traits/remove_cv.h>
1820
#include <__utility/declval.h>
1921
#include <__utility/pair.h>
@@ -34,23 +36,27 @@ _LIBCPP_BEGIN_NAMESPACE_STD
3436
template <class _Tp>
3537
inline constexpr bool __is_cv_std_pair = __is_pair_v<remove_cv_t<_Tp>>;
3638

37-
template <class _Tp, class = void>
39+
template <class _Tp, bool _IsCvStdPair = __is_cv_std_pair<_Tp>>
3840
struct __uses_allocator_construction_args;
3941

42+
# if _LIBCPP_STD_VER >= 20
43+
template <class _Tp>
44+
using __uses_allocator_construction_args_recursive _LIBCPP_NODEBUG = __uses_allocator_construction_args<_Tp>;
45+
# else
46+
template <class _Tp>
47+
using __uses_allocator_construction_args_recursive _LIBCPP_NODEBUG = __uses_allocator_construction_args<_Tp, false>;
48+
# endif
49+
4050
namespace __uses_allocator_detail {
4151

4252
template <class _Ap, class _Bp>
4353
void __fun(const pair<_Ap, _Bp>&);
4454

55+
template <class, class = void>
56+
inline constexpr bool __convertible_to_const_pair_ref = false;
4557
template <class _Tp>
46-
decltype(__uses_allocator_detail::__fun(std::declval<_Tp>()), true_type()) __convertible_to_const_pair_ref_impl(int);
47-
48-
template <class>
49-
false_type __convertible_to_const_pair_ref_impl(...);
50-
51-
template <class _Tp>
52-
inline constexpr bool __convertible_to_const_pair_ref =
53-
decltype(__uses_allocator_detail::__convertible_to_const_pair_ref_impl<_Tp>(0))::value;
58+
inline constexpr bool
59+
__convertible_to_const_pair_ref<_Tp, decltype(__uses_allocator_detail::__fun(std::declval<_Tp>()))> = true;
5460

5561
# if _LIBCPP_STD_VER >= 23
5662
template <class _Tp, class _Up>
@@ -67,21 +73,25 @@ template <class _Type, class _Alloc, class... _Args>
6773
_LIBCPP_HIDE_FROM_ABI constexpr _Type __make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args);
6874

6975
template <class _Pair>
70-
struct __uses_allocator_construction_args<_Pair, __enable_if_t<__is_cv_std_pair<_Pair>>> {
76+
struct __uses_allocator_construction_args<_Pair, true> {
7177
template <class _Alloc, class _Tuple1, class _Tuple2>
7278
static _LIBCPP_HIDE_FROM_ABI constexpr auto
7379
__apply(const _Alloc& __alloc, piecewise_construct_t, _Tuple1&& __x, _Tuple2&& __y) noexcept {
7480
return std::make_tuple(
81+
# if _LIBCPP_STD_VER >= 20
7582
piecewise_construct,
83+
# else
84+
reference_wrapper{piecewise_construct},
85+
# endif
7686
std::apply(
7787
[&__alloc](auto&&... __args1) {
78-
return __uses_allocator_construction_args<typename _Pair::first_type>::__apply(
88+
return __uses_allocator_construction_args_recursive<typename _Pair::first_type>::__apply(
7989
__alloc, std::forward<decltype(__args1)>(__args1)...);
8090
},
8191
std::forward<_Tuple1>(__x)),
8292
std::apply(
8393
[&__alloc](auto&&... __args2) {
84-
return __uses_allocator_construction_args<typename _Pair::second_type>::__apply(
94+
return __uses_allocator_construction_args_recursive<typename _Pair::second_type>::__apply(
8595
__alloc, std::forward<decltype(__args2)>(__args2)...);
8696
},
8797
std::forward<_Tuple2>(__y)));
@@ -170,7 +180,7 @@ struct __uses_allocator_construction_args<_Pair, __enable_if_t<__is_cv_std_pair<
170180
};
171181

172182
template <class _Type>
173-
struct __uses_allocator_construction_args<_Type, __enable_if_t<!__is_cv_std_pair<_Type>>> {
183+
struct __uses_allocator_construction_args<_Type, false> {
174184
template <class _Alloc, class... _Args>
175185
static _LIBCPP_HIDE_FROM_ABI constexpr auto __apply(const _Alloc& __alloc, _Args&&... __args) noexcept {
176186
if constexpr (!uses_allocator_v<remove_cv_t<_Type>, _Alloc> && is_constructible_v<_Type, _Args...>) {

libcxx/include/__memory_resource/polymorphic_allocator.h

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
#include <__cstddef/byte.h>
1515
#include <__cstddef/max_align_t.h>
1616
#include <__fwd/pair.h>
17+
#include <__memory/construct_at.h>
18+
#include <__memory/uses_allocator_construction.h>
1719
#include <__memory_resource/memory_resource.h>
1820
#include <__new/exceptions.h>
1921
#include <__new/placement_new_delete.h>
22+
#include <__type_traits/remove_cv.h>
2023
#include <__utility/exception_guard.h>
2124
#include <__utility/piecewise_construct.h>
2225
#include <limits>
@@ -35,6 +38,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
3538

3639
namespace pmr {
3740

41+
template <class _Type, class _Alloc, class... _Args>
42+
_LIBCPP_HIDE_FROM_ABI _Type*
43+
__uninitialized_construct_using_allocator_nocv(_Type* __ptr, const _Alloc& __alloc, _Args&&... __args) {
44+
return std::apply(
45+
[__ptr_nocv = const_cast<remove_cv_t<_Type>*>(__ptr)](auto&&... __xs) {
46+
return std::__construct_at(__ptr_nocv, std::forward<decltype(__xs)>(__xs)...);
47+
},
48+
__uses_allocator_construction_args<_Type>::__apply(__alloc, std::forward<_Args>(__args)...));
49+
}
50+
3851
// [mem.poly.allocator.class]
3952

4053
template <class _ValueType
@@ -122,24 +135,14 @@ class _LIBCPP_AVAILABILITY_PMR polymorphic_allocator {
122135

123136
template <class _Tp, class... _Ts>
124137
_LIBCPP_HIDE_FROM_ABI void construct(_Tp* __p, _Ts&&... __args) {
125-
std::__user_alloc_construct_impl(
126-
typename __uses_alloc_ctor<_Tp, polymorphic_allocator&, _Ts...>::type(),
127-
__p,
128-
*this,
129-
std::forward<_Ts>(__args)...);
138+
std::pmr::__uninitialized_construct_using_allocator_nocv(__p, *this, std::forward<_Ts>(__args)...);
130139
}
131140

132141
template <class _T1, class _T2, class... _Args1, class... _Args2>
133142
_LIBCPP_HIDE_FROM_ABI void
134143
construct(pair<_T1, _T2>* __p, piecewise_construct_t, tuple<_Args1...> __x, tuple<_Args2...> __y) {
135-
::new ((void*)__p) pair<_T1, _T2>(
136-
piecewise_construct,
137-
__transform_tuple(typename __uses_alloc_ctor< _T1, polymorphic_allocator&, _Args1... >::type(),
138-
std::move(__x),
139-
make_index_sequence<sizeof...(_Args1)>()),
140-
__transform_tuple(typename __uses_alloc_ctor< _T2, polymorphic_allocator&, _Args2... >::type(),
141-
std::move(__y),
142-
make_index_sequence<sizeof...(_Args2)>()));
144+
std::pmr::__uninitialized_construct_using_allocator_nocv(
145+
__p, *this, piecewise_construct, tuple<_Args1&&...>(std::move(__x)), tuple<_Args2&&...>(std::move(__y)));
143146
}
144147

145148
template <class _T1, class _T2>
@@ -193,26 +196,6 @@ class _LIBCPP_AVAILABILITY_PMR polymorphic_allocator {
193196
# endif
194197

195198
private:
196-
template <class... _Args, size_t... _Is>
197-
_LIBCPP_HIDE_FROM_ABI tuple<_Args&&...>
198-
__transform_tuple(integral_constant<int, 0>, tuple<_Args...>&& __t, index_sequence<_Is...>) {
199-
return std::forward_as_tuple(std::get<_Is>(std::move(__t))...);
200-
}
201-
202-
template <class... _Args, size_t... _Is>
203-
_LIBCPP_HIDE_FROM_ABI tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>
204-
__transform_tuple(integral_constant<int, 1>, tuple<_Args...>&& __t, index_sequence<_Is...>) {
205-
using _Tup = tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>;
206-
return _Tup(allocator_arg, *this, std::get<_Is>(std::move(__t))...);
207-
}
208-
209-
template <class... _Args, size_t... _Is>
210-
_LIBCPP_HIDE_FROM_ABI tuple<_Args&&..., polymorphic_allocator&>
211-
__transform_tuple(integral_constant<int, 2>, tuple<_Args...>&& __t, index_sequence<_Is...>) {
212-
using _Tup = tuple<_Args&&..., polymorphic_allocator&>;
213-
return _Tup(std::get<_Is>(std::move(__t))..., *this);
214-
}
215-
216199
_LIBCPP_HIDE_FROM_ABI size_t __max_size() const noexcept {
217200
return numeric_limits<size_t>::max() / sizeof(value_type);
218201
}

libcxx/include/tuple

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,11 @@ public:
373373
static_assert(!is_reference<_Hp>::value, "Attempted to default construct a reference element in a tuple");
374374
}
375375

376+
template <class _Alloc, class... _Args>
377+
_LIBCPP_HIDE_FROM_ABI __tuple_leaf(integral_constant<int, -1>, const _Alloc&, _Args&&...) {
378+
static_assert(false, "If uses_allocator_v<T, A> is true, T has to be allocator-constructible");
379+
}
380+
376381
template <class _Alloc>
377382
_LIBCPP_HIDE_FROM_ABI constexpr __tuple_leaf(integral_constant<int, 0>, const _Alloc&) : __value_() {
378383
static_assert(!is_reference<_Hp>::value, "Attempted to default construct a reference element in a tuple");
@@ -447,6 +452,11 @@ public:
447452

448453
_LIBCPP_HIDE_FROM_ABI constexpr __tuple_leaf() noexcept(is_nothrow_default_constructible<_Hp>::value) {}
449454

455+
template <class _Alloc, class... _Args>
456+
_LIBCPP_HIDE_FROM_ABI __tuple_leaf(integral_constant<int, -1>, const _Alloc&, _Args&&...) {
457+
static_assert(false, "If uses_allocator_v<T, A> is true, T has to be allocator-constructible");
458+
}
459+
450460
template <class _Alloc>
451461
_LIBCPP_HIDE_FROM_ABI constexpr __tuple_leaf(integral_constant<int, 0>, const _Alloc&) {}
452462

libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_piecewise_pair_evil.pass.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ template <class T>
3030
struct EvilAlloc {
3131
explicit EvilAlloc() : inner_(std::pmr::null_memory_resource()) {}
3232

33-
EvilAlloc(std::pmr::polymorphic_allocator<T>& a) : inner_(a) {}
33+
EvilAlloc(std::pmr::polymorphic_allocator<T>&) = delete;
3434
EvilAlloc(std::pmr::polymorphic_allocator<T>&& a) : inner_(a) {}
35-
EvilAlloc(std::pmr::polymorphic_allocator<T> const& a) = delete;
36-
EvilAlloc(std::pmr::polymorphic_allocator<T> const&& a) = delete;
35+
EvilAlloc(std::pmr::polymorphic_allocator<T> const& a) : inner_(a) {}
36+
EvilAlloc(std::pmr::polymorphic_allocator<T> const&&) = delete;
3737

3838
using value_type = T;
3939
template <class U>

0 commit comments

Comments
 (0)