Skip to content

Commit 54f4ac6

Browse files
frederick-vs-jaStephanTLavavejCaseyCarter
authored
Implement LWG-3525: uses_allocator_construction_args fails to handle types convertible to pair (#2639)
Co-authored-by: Stephan T. Lavavej <[email protected]> Co-authored-by: Casey Carter <[email protected]>
1 parent 3442544 commit 54f4ac6

File tree

3 files changed

+114
-0
lines changed

3 files changed

+114
-0
lines changed

stl/inc/xmemory

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2067,6 +2067,30 @@ typename _Container::size_type _Erase_nodes_if(_Container& _Cont, _Pr _Pred) {
20672067
return _Old_size - _Cont.size();
20682068
}
20692069

2070+
template <class _Ty1, class _Ty2>
2071+
void _Deduce_as_pair(const pair<_Ty1, _Ty2>&); // not defined
2072+
2073+
template <class _Ty, class = void>
2074+
_INLINE_VAR constexpr bool _Is_deducible_as_pair = false;
2075+
2076+
template <class _Ty>
2077+
_INLINE_VAR constexpr bool _Is_deducible_as_pair<_Ty, decltype(_Deduce_as_pair(_STD declval<_Ty>()))> = true;
2078+
2079+
template <class _Ty>
2080+
const _Ty& _Normally_bind(_Identity_t<const _Ty&>); // not defined
2081+
2082+
template <class _Ty>
2083+
_Ty&& _Normally_bind(_Identity_t<_Ty&&>); // not defined
2084+
2085+
template <class _Ty, class _Uty>
2086+
using _Normally_bound_ref = decltype(_Normally_bind<_Ty>(_STD declval<_Uty>()));
2087+
2088+
template <class _Ty, class _Uty, class = void>
2089+
_INLINE_VAR constexpr bool _Is_normally_bindable = false;
2090+
2091+
template <class _Ty, class _Uty>
2092+
_INLINE_VAR constexpr bool _Is_normally_bindable<_Ty, _Uty, void_t<_Normally_bound_ref<_Ty, _Uty>>> = true;
2093+
20702094
#if _HAS_CXX20
20712095
template <class _Ty, class _Alloc, class... _Types, enable_if_t<!_Is_specialization_v<_Ty, pair>, int> = 0>
20722096
_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Types&&... _Args) noexcept {
@@ -2099,6 +2123,10 @@ _NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, co
20992123
template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_specialization_v<_Ty, pair>, int> = 0>
21002124
_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, pair<_Uty1, _Uty2>&& _Pair) noexcept;
21012125

2126+
template <class _Ty, class _Alloc, class _Uty,
2127+
enable_if_t<_Is_specialization_v<_Ty, pair> && !_Is_deducible_as_pair<_Uty&>, int> = 0>
2128+
_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Uty&& _Ux) noexcept;
2129+
21022130
template <class _Ty, class _Alloc, class _Tuple1, class _Tuple2, enable_if_t<_Is_specialization_v<_Ty, pair>, int> = 0>
21032131
_NODISCARD constexpr auto uses_allocator_construction_args(
21042132
const _Alloc& _Al, piecewise_construct_t, _Tuple1&& _Tup1, _Tuple2&& _Tup2) noexcept {
@@ -2156,6 +2184,43 @@ _NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, pa
21562184
_STD uses_allocator_construction_args<typename _Ty::second_type>(_Al, _STD get<1>(_STD move(_Pair))));
21572185
}
21582186

2187+
template <class _Ty, class _Alloc, class _Uty,
2188+
enable_if_t<_Is_specialization_v<_Ty, pair> && !_Is_deducible_as_pair<_Uty&>, int>>
2189+
_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Uty&& _Ux) noexcept {
2190+
struct _Pair_remaker {
2191+
const _Alloc& _Al;
2192+
_Uty& _Ux;
2193+
2194+
constexpr operator remove_cv_t<_Ty>() const {
2195+
using _Pair_t = remove_cv_t<_Ty>;
2196+
static_assert(_Is_normally_bindable<_Pair_t, _Uty>,
2197+
"The argument must be bindable to a reference to the std::pair type.");
2198+
2199+
using _Pair_first_t = typename _Pair_t::first_type;
2200+
using _Pair_second_t = typename _Pair_t::second_type;
2201+
using _Pair_ref_t = _Normally_bound_ref<_Pair_t, _Uty>;
2202+
_Pair_ref_t _Pair_ref = _STD forward<_Uty>(_Ux);
2203+
if constexpr (is_same_v<_Pair_ref_t, const _Pair_t&>) {
2204+
// equivalent to
2205+
// return _STD make_obj_using_allocator<_Pair_t>(_Al, _Pair_ref);
2206+
return _Pair_t{piecewise_construct,
2207+
_STD uses_allocator_construction_args<_Pair_first_t>(_Al, _Pair_ref.first),
2208+
_STD uses_allocator_construction_args<_Pair_second_t>(_Al, _Pair_ref.second)};
2209+
} else {
2210+
// equivalent to
2211+
// return _STD make_obj_using_allocator<_Pair_t>(_Al, _STD move(_Pair_ref));
2212+
return _Pair_t{piecewise_construct,
2213+
_STD uses_allocator_construction_args<_Pair_first_t>(_Al, _STD get<0>(_STD move(_Pair_ref))),
2214+
_STD uses_allocator_construction_args<_Pair_second_t>(_Al, _STD get<1>(_STD move(_Pair_ref)))};
2215+
}
2216+
}
2217+
};
2218+
2219+
// equivalent to
2220+
// return _STD make_tuple(_Pair_remaker{_Al, _Ux});
2221+
return tuple<_Pair_remaker>({_Al, _Ux});
2222+
}
2223+
21592224
template <class _Ty, class _Alloc, class... _Types>
21602225
_NODISCARD constexpr _Ty make_obj_using_allocator(const _Alloc& _Al, _Types&&... _Args) {
21612226
return _STD make_from_tuple<_Ty>(_STD uses_allocator_construction_args<_Ty>(_Al, _STD forward<_Types>(_Args)...));

stl/inc/xpolymorphic_allocator.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,24 @@ void _Uses_allocator_construct(
104104
_Uses_allocator_construct_pair(_Ptr, _Outer, _Inner, _STD forward_as_tuple(_STD forward<_Uty>(_Pair.first)),
105105
_STD forward_as_tuple(_STD forward<_Vty>(_Pair.second)));
106106
}
107+
108+
template <class _Ty1, class _Ty2, class _Outer_alloc, class _Inner_alloc, class _Uty,
109+
enable_if_t<!_Is_deducible_as_pair<_Uty>, int> = 0>
110+
void _Uses_allocator_construct(pair<_Ty1, _Ty2>* const _Ptr, _Outer_alloc& _Outer, _Inner_alloc& _Inner, _Uty&& _Ux) {
111+
// uses-allocator construction of pair by alloc _Outer propagating alloc _Inner, non-pair argument
112+
static_assert(_Is_normally_bindable<pair<_Ty1, _Ty2>, _Uty>,
113+
"The argument must be bindable to a reference to the std::pair type.");
114+
115+
using _Pair_ref_t = _Normally_bound_ref<pair<_Ty1, _Ty2>, _Uty>;
116+
_Pair_ref_t _Pair_ref = _STD forward<_Uty>(_Ux);
117+
if constexpr (is_same_v<_Pair_ref_t, const pair<_Ty1, _Ty2>&>) {
118+
_Uses_allocator_construct_pair(
119+
_Ptr, _Outer, _Inner, _STD forward_as_tuple(_Pair_ref.first), _STD forward_as_tuple(_Pair_ref.second));
120+
} else {
121+
_Uses_allocator_construct_pair(_Ptr, _Outer, _Inner, _STD forward_as_tuple(_STD forward<_Ty1>(_Pair_ref.first)),
122+
_STD forward_as_tuple(_STD forward<_Ty2>(_Pair_ref.second)));
123+
}
124+
}
107125
#endif // !_HAS_CXX20
108126

109127
#if _HAS_CXX17

tests/std/tests/P0220R1_polymorphic_memory_resources/test.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,6 +1451,35 @@ namespace {
14511451
pmr_container_test<std::pmr::wsmatch>();
14521452
}
14531453
} // namespace containers
1454+
1455+
namespace map_containers {
1456+
template <class T>
1457+
void pair_conversion_test() {
1458+
struct pair_conv {
1459+
operator std::pair<const int, int>() const {
1460+
return {};
1461+
}
1462+
};
1463+
1464+
struct mem_pair_conv {
1465+
std::pair<const int, int> pair_{1, 42};
1466+
operator const std::pair<const int, int>&() const {
1467+
return pair_;
1468+
}
1469+
};
1470+
1471+
T cont;
1472+
cont.emplace(pair_conv{});
1473+
cont.emplace(mem_pair_conv{});
1474+
}
1475+
1476+
void test() {
1477+
pair_conversion_test<std::pmr::map<int, int>>();
1478+
pair_conversion_test<std::pmr::multimap<int, int>>();
1479+
pair_conversion_test<std::pmr::unordered_map<int, int>>();
1480+
pair_conversion_test<std::pmr::unordered_multimap<int, int>>();
1481+
}
1482+
} // namespace map_containers
14541483
} // unnamed namespace
14551484

14561485
int main() {
@@ -1491,4 +1520,6 @@ int main() {
14911520
pool::allocate_deallocate::test();
14921521

14931522
containers::test();
1523+
1524+
map_containers::test();
14941525
}

0 commit comments

Comments
 (0)