diff --git a/libcxx/include/__flat_map/flat_map.h b/libcxx/include/__flat_map/flat_map.h index 97e863c85371..8f01882934b7 100644 --- a/libcxx/include/__flat_map/flat_map.h +++ b/libcxx/include/__flat_map/flat_map.h @@ -1012,11 +1012,11 @@ private: } _LIBCPP_HIDE_FROM_ABI void __reserve(size_t __size) { - if constexpr (requires { __containers_.keys.reserve(__size); }) { + if constexpr (__container_traits<_KeyContainer>::__reservable) { __containers_.keys.reserve(__size); } - if constexpr (requires { __containers_.values.reserve(__size); }) { + if constexpr (__container_traits<_MappedContainer>::__reservable) { __containers_.values.reserve(__size); } } diff --git a/libcxx/include/__flat_map/flat_multimap.h b/libcxx/include/__flat_map/flat_multimap.h index d56d2bdb6046..0af6aac00c38 100644 --- a/libcxx/include/__flat_map/flat_multimap.h +++ b/libcxx/include/__flat_map/flat_multimap.h @@ -827,11 +827,11 @@ private: } _LIBCPP_HIDE_FROM_ABI void __reserve(size_t __size) { - if constexpr (requires { __containers_.keys.reserve(__size); }) { + if constexpr (__container_traits<_KeyContainer>::__reservable) { __containers_.keys.reserve(__size); } - if constexpr (requires { __containers_.values.reserve(__size); }) { + if constexpr (__container_traits<_MappedContainer>::__reservable) { __containers_.values.reserve(__size); } } diff --git a/libcxx/include/__flat_set/flat_multiset.h b/libcxx/include/__flat_set/flat_multiset.h index 0fed377b25e5..44d8af05a56a 100644 --- a/libcxx/include/__flat_set/flat_multiset.h +++ b/libcxx/include/__flat_set/flat_multiset.h @@ -667,7 +667,7 @@ private: } _LIBCPP_HIDE_FROM_ABI void __reserve(size_t __size) { - if constexpr (requires { __keys_.reserve(__size); }) { + if constexpr (__container_traits<_KeyContainer>::__reservable) { __keys_.reserve(__size); } } diff --git a/libcxx/include/__flat_set/flat_set.h b/libcxx/include/__flat_set/flat_set.h index 35ca8feab0fd..b8edd8eee0cf 100644 --- a/libcxx/include/__flat_set/flat_set.h +++ b/libcxx/include/__flat_set/flat_set.h @@ -698,7 +698,7 @@ private: } _LIBCPP_HIDE_FROM_ABI void __reserve(size_t __size) { - if constexpr (requires { __keys_.reserve(__size); }) { + if constexpr (__container_traits<_KeyContainer>::__reservable) { __keys_.reserve(__size); } } diff --git a/libcxx/include/__type_traits/container_traits.h b/libcxx/include/__type_traits/container_traits.h index 5262cef94c61..ed4e19c67503 100644 --- a/libcxx/include/__type_traits/container_traits.h +++ b/libcxx/include/__type_traits/container_traits.h @@ -36,6 +36,9 @@ struct __container_traits { // `insert(...)` or `emplace(...)` has strong exception guarantee, that is, if the function // exits via an exception, the original container is unaffected static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = false; + + // A trait that tells whether a container supports `reserve(n)` member function. + static _LIBCPP_CONSTEXPR const bool __reservable = false; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__vector/container_traits.h b/libcxx/include/__vector/container_traits.h index 337b9c5c8368..20f2b9ef042c 100644 --- a/libcxx/include/__vector/container_traits.h +++ b/libcxx/include/__vector/container_traits.h @@ -32,6 +32,8 @@ struct __container_traits > { // the effects are unspecified. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = is_nothrow_move_constructible<_Tp>::value || __is_cpp17_copy_insertable_v<_Allocator>; + + static _LIBCPP_CONSTEXPR const bool __reservable = true; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/deque b/libcxx/include/deque index d8645d06ae59..5b9aaf8788a6 100644 --- a/libcxx/include/deque +++ b/libcxx/include/deque @@ -2631,6 +2631,8 @@ struct __container_traits > { // non-Cpp17CopyInsertable T, the effects are unspecified. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = is_nothrow_move_constructible<_Tp>::value || __is_cpp17_copy_insertable_v<_Allocator>; + + static _LIBCPP_CONSTEXPR const bool __reservable = false; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list index fc4a9949f37b..5046de27a9da 100644 --- a/libcxx/include/forward_list +++ b/libcxx/include/forward_list @@ -1561,6 +1561,8 @@ struct __container_traits > { // - If an exception is thrown by an insert() or emplace() function while inserting a single element, that // function has no effects. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; + + static _LIBCPP_CONSTEXPR const bool __reservable = false; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/list b/libcxx/include/list index d1da347b9bd1..98610f59ed74 100644 --- a/libcxx/include/list +++ b/libcxx/include/list @@ -1718,6 +1718,8 @@ struct __container_traits > { // - If an exception is thrown by an insert() or emplace() function while inserting a single element, that // function has no effects. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; + + static _LIBCPP_CONSTEXPR const bool __reservable = false; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/map b/libcxx/include/map index 8c5799fbe6f1..73bf672d3a08 100644 --- a/libcxx/include/map +++ b/libcxx/include/map @@ -1548,6 +1548,8 @@ struct __container_traits > { // For associative containers, if an exception is thrown by any operation from within // an insert or emplace function inserting a single element, the insertion has no effect. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; + + static _LIBCPP_CONSTEXPR const bool __reservable = false; }; template @@ -2071,6 +2073,8 @@ struct __container_traits > { // For associative containers, if an exception is thrown by any operation from within // an insert or emplace function inserting a single element, the insertion has no effect. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; + + static _LIBCPP_CONSTEXPR const bool __reservable = false; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/set b/libcxx/include/set index 83c8ed59065c..aeea98adf582 100644 --- a/libcxx/include/set +++ b/libcxx/include/set @@ -1030,6 +1030,8 @@ struct __container_traits > { // For associative containers, if an exception is thrown by any operation from within // an insert or emplace function inserting a single element, the insertion has no effect. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; + + static _LIBCPP_CONSTEXPR const bool __reservable = false; }; template @@ -1499,6 +1501,8 @@ struct __container_traits > { // For associative containers, if an exception is thrown by any operation from within // an insert or emplace function inserting a single element, the insertion has no effect. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true; + + static _LIBCPP_CONSTEXPR const bool __reservable = false; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map index 61c89a0ca73b..b7f333e5f117 100644 --- a/libcxx/include/unordered_map +++ b/libcxx/include/unordered_map @@ -1843,6 +1843,8 @@ struct __container_traits > { // inserting a single element, the insertion has no effect. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = __is_nothrow_invocable_v<_Hash, const _Key&>; + + static _LIBCPP_CONSTEXPR const bool __reservable = true; }; template > // inserting a single element, the insertion has no effect. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = __is_nothrow_invocable_v<_Hash, const _Key&>; + + static _LIBCPP_CONSTEXPR const bool __reservable = true; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set index e97e6e8042e6..1412badbe37f 100644 --- a/libcxx/include/unordered_set +++ b/libcxx/include/unordered_set @@ -1196,6 +1196,8 @@ struct __container_traits > { // inserting a single element, the insertion has no effect. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = __is_nothrow_invocable_v<_Hash, const _Value&>; + + static _LIBCPP_CONSTEXPR const bool __reservable = true; }; template , class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> > @@ -1816,6 +1818,8 @@ struct __container_traits > { // inserting a single element, the insertion has no effect. static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = __is_nothrow_invocable_v<_Hash, const _Value&>; + + static _LIBCPP_CONSTEXPR const bool __reservable = true; }; _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_iter_iter.pass.cpp index 8455b19475fe..ccce117c90fc 100644 --- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_iter_iter.pass.cpp +++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_iter_iter.pass.cpp @@ -13,6 +13,7 @@ // template // void insert(InputIterator first, InputIterator last); +#include #include #include #include @@ -75,6 +76,7 @@ void test() { M expected2{{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}}; assert(m == expected2); } + int main(int, char**) { test, std::vector>(); test, std::vector>(); @@ -85,5 +87,12 @@ int main(int, char**) { auto insert_func = [](auto& m, const auto& newValues) { m.insert(newValues.begin(), newValues.end()); }; test_insert_range_exception_guarantee(insert_func); } + { + std::flat_map, SillyReserveVector, SillyReserveVector> m{{1, 1}, {2, 2}}; + std::vector> v{{3, 3}, {4, 4}}; + m.insert(v.begin(), v.end()); + assert(std::ranges::equal(m, std::vector>{{1, 1}, {2, 2}, {3, 3}, {4, 4}})); + } + return 0; } diff --git a/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.modifiers/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.modifiers/insert_iter_iter.pass.cpp index ae031bd010f7..30cb89dadbfe 100644 --- a/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.modifiers/insert_iter_iter.pass.cpp +++ b/libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.modifiers/insert_iter_iter.pass.cpp @@ -16,6 +16,7 @@ // void insert(InputIterator first, InputIterator last); #include +#include #include #include #include @@ -105,5 +106,11 @@ int main(int, char**) { auto insert_func = [](auto& m, const auto& newValues) { m.insert(newValues.begin(), newValues.end()); }; test_insert_range_exception_guarantee(insert_func); } + { + std::flat_multimap, SillyReserveVector, SillyReserveVector> m{{1, 1}, {2, 2}}; + std::vector> v{{3, 3}, {4, 4}}; + m.insert(v.begin(), v.end()); + assert(std::ranges::equal(m, std::vector>{{1, 1}, {2, 2}, {3, 3}, {4, 4}})); + } return 0; } diff --git a/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.modifiers/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.modifiers/insert_iter_iter.pass.cpp index 3505e097cca6..93815686787c 100644 --- a/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.modifiers/insert_iter_iter.pass.cpp +++ b/libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.modifiers/insert_iter_iter.pass.cpp @@ -14,6 +14,7 @@ // void insert(InputIterator first, InputIterator last); #include +#include #include #include #include @@ -79,6 +80,12 @@ void test() { test_one>(); test_one>(); test_one>>(); + { + std::flat_multiset, SillyReserveVector> m{1, 2}; + std::vector v{3, 4}; + m.insert(v.begin(), v.end()); + assert(std::ranges::equal(m, std::vector{1, 2, 3, 4})); + } } void test_exception() { diff --git a/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.modifiers/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.modifiers/insert_iter_iter.pass.cpp index 8063686c960e..18bdb798de01 100644 --- a/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.modifiers/insert_iter_iter.pass.cpp +++ b/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.modifiers/insert_iter_iter.pass.cpp @@ -14,6 +14,7 @@ // void insert(InputIterator first, InputIterator last); #include +#include #include #include #include @@ -79,6 +80,12 @@ void test() { test_one>(); test_one>(); test_one>>(); + { + std::flat_set, SillyReserveVector> m{1, 2}; + std::vector v{3, 4}; + m.insert(v.begin(), v.end()); + assert(std::ranges::equal(m, std::vector{1, 2, 3, 4})); + } } void test_exception() { diff --git a/libcxx/test/std/containers/container.adaptors/flat_helpers.h b/libcxx/test/std/containers/container.adaptors/flat_helpers.h index 9cd408ef960a..19d637052203 100644 --- a/libcxx/test/std/containers/container.adaptors/flat_helpers.h +++ b/libcxx/test/std/containers/container.adaptors/flat_helpers.h @@ -25,6 +25,13 @@ struct CopyOnlyVector : std::vector { CopyOnlyVector& operator=(CopyOnlyVector& other) { return this->operator=(other); } }; +template +struct SillyReserveVector : std::vector { + using std::vector::vector; + + void reserve(size_t) { this->clear(); } +}; + template struct Transparent { T t;