Skip to content

Commit 692c5cd

Browse files
authored
[libc++] Do not call reserve in flat containers if underlying container is user defined (#140379)
This is brought up in the LWG reflector. We currently call `reserve` if the underlying container has one. But the spec does not specify what `reserve` should do for Sequence Container. So in theory if the underlying container is user defined type and it can have a function called `reserve` which does something completely different. The fix is to just call `reserve` for STL containers if it has one
1 parent 90a52f4 commit 692c5cd

File tree

18 files changed

+70
-6
lines changed

18 files changed

+70
-6
lines changed

libcxx/include/__flat_map/flat_map.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,11 +1012,11 @@ class flat_map {
10121012
}
10131013

10141014
_LIBCPP_HIDE_FROM_ABI void __reserve(size_t __size) {
1015-
if constexpr (requires { __containers_.keys.reserve(__size); }) {
1015+
if constexpr (__container_traits<_KeyContainer>::__reservable) {
10161016
__containers_.keys.reserve(__size);
10171017
}
10181018

1019-
if constexpr (requires { __containers_.values.reserve(__size); }) {
1019+
if constexpr (__container_traits<_MappedContainer>::__reservable) {
10201020
__containers_.values.reserve(__size);
10211021
}
10221022
}

libcxx/include/__flat_map/flat_multimap.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -827,11 +827,11 @@ class flat_multimap {
827827
}
828828

829829
_LIBCPP_HIDE_FROM_ABI void __reserve(size_t __size) {
830-
if constexpr (requires { __containers_.keys.reserve(__size); }) {
830+
if constexpr (__container_traits<_KeyContainer>::__reservable) {
831831
__containers_.keys.reserve(__size);
832832
}
833833

834-
if constexpr (requires { __containers_.values.reserve(__size); }) {
834+
if constexpr (__container_traits<_MappedContainer>::__reservable) {
835835
__containers_.values.reserve(__size);
836836
}
837837
}

libcxx/include/__flat_set/flat_multiset.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ class flat_multiset {
667667
}
668668

669669
_LIBCPP_HIDE_FROM_ABI void __reserve(size_t __size) {
670-
if constexpr (requires { __keys_.reserve(__size); }) {
670+
if constexpr (__container_traits<_KeyContainer>::__reservable) {
671671
__keys_.reserve(__size);
672672
}
673673
}

libcxx/include/__flat_set/flat_set.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,7 @@ class flat_set {
698698
}
699699

700700
_LIBCPP_HIDE_FROM_ABI void __reserve(size_t __size) {
701-
if constexpr (requires { __keys_.reserve(__size); }) {
701+
if constexpr (__container_traits<_KeyContainer>::__reservable) {
702702
__keys_.reserve(__size);
703703
}
704704
}

libcxx/include/__type_traits/container_traits.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ struct __container_traits {
3636
// `insert(...)` or `emplace(...)` has strong exception guarantee, that is, if the function
3737
// exits via an exception, the original container is unaffected
3838
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = false;
39+
40+
// A trait that tells whether a container supports `reserve(n)` member function.
41+
static _LIBCPP_CONSTEXPR const bool __reservable = false;
3942
};
4043

4144
_LIBCPP_END_NAMESPACE_STD

libcxx/include/__vector/container_traits.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ struct __container_traits<vector<_Tp, _Allocator> > {
3232
// the effects are unspecified.
3333
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
3434
is_nothrow_move_constructible<_Tp>::value || __is_cpp17_copy_insertable_v<_Allocator>;
35+
36+
static _LIBCPP_CONSTEXPR const bool __reservable = true;
3537
};
3638

3739
_LIBCPP_END_NAMESPACE_STD

libcxx/include/deque

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2631,6 +2631,8 @@ struct __container_traits<deque<_Tp, _Allocator> > {
26312631
// non-Cpp17CopyInsertable T, the effects are unspecified.
26322632
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
26332633
is_nothrow_move_constructible<_Tp>::value || __is_cpp17_copy_insertable_v<_Allocator>;
2634+
2635+
static _LIBCPP_CONSTEXPR const bool __reservable = false;
26342636
};
26352637

26362638
_LIBCPP_END_NAMESPACE_STD

libcxx/include/forward_list

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,8 @@ struct __container_traits<forward_list<_Tp, _Allocator> > {
15611561
// - If an exception is thrown by an insert() or emplace() function while inserting a single element, that
15621562
// function has no effects.
15631563
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true;
1564+
1565+
static _LIBCPP_CONSTEXPR const bool __reservable = false;
15641566
};
15651567

15661568
_LIBCPP_END_NAMESPACE_STD

libcxx/include/list

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,6 +1718,8 @@ struct __container_traits<list<_Tp, _Allocator> > {
17181718
// - If an exception is thrown by an insert() or emplace() function while inserting a single element, that
17191719
// function has no effects.
17201720
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true;
1721+
1722+
static _LIBCPP_CONSTEXPR const bool __reservable = false;
17211723
};
17221724

17231725
_LIBCPP_END_NAMESPACE_STD

libcxx/include/map

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,6 +1548,8 @@ struct __container_traits<map<_Key, _Tp, _Compare, _Allocator> > {
15481548
// For associative containers, if an exception is thrown by any operation from within
15491549
// an insert or emplace function inserting a single element, the insertion has no effect.
15501550
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true;
1551+
1552+
static _LIBCPP_CONSTEXPR const bool __reservable = false;
15511553
};
15521554

15531555
template <class _Key, class _Tp, class _Compare, class _Allocator>
@@ -2071,6 +2073,8 @@ struct __container_traits<multimap<_Key, _Tp, _Compare, _Allocator> > {
20712073
// For associative containers, if an exception is thrown by any operation from within
20722074
// an insert or emplace function inserting a single element, the insertion has no effect.
20732075
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true;
2076+
2077+
static _LIBCPP_CONSTEXPR const bool __reservable = false;
20742078
};
20752079

20762080
_LIBCPP_END_NAMESPACE_STD

libcxx/include/set

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,8 @@ struct __container_traits<set<_Key, _Compare, _Allocator> > {
10301030
// For associative containers, if an exception is thrown by any operation from within
10311031
// an insert or emplace function inserting a single element, the insertion has no effect.
10321032
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true;
1033+
1034+
static _LIBCPP_CONSTEXPR const bool __reservable = false;
10331035
};
10341036

10351037
template <class _Key, class _Compare, class _Allocator>
@@ -1499,6 +1501,8 @@ struct __container_traits<multiset<_Key, _Compare, _Allocator> > {
14991501
// For associative containers, if an exception is thrown by any operation from within
15001502
// an insert or emplace function inserting a single element, the insertion has no effect.
15011503
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee = true;
1504+
1505+
static _LIBCPP_CONSTEXPR const bool __reservable = false;
15021506
};
15031507

15041508
_LIBCPP_END_NAMESPACE_STD

libcxx/include/unordered_map

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,6 +1843,8 @@ struct __container_traits<unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc> > {
18431843
// inserting a single element, the insertion has no effect.
18441844
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
18451845
__is_nothrow_invocable_v<_Hash, const _Key&>;
1846+
1847+
static _LIBCPP_CONSTEXPR const bool __reservable = true;
18461848
};
18471849

18481850
template <class _Key,
@@ -2543,6 +2545,8 @@ struct __container_traits<unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc> >
25432545
// inserting a single element, the insertion has no effect.
25442546
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
25452547
__is_nothrow_invocable_v<_Hash, const _Key&>;
2548+
2549+
static _LIBCPP_CONSTEXPR const bool __reservable = true;
25462550
};
25472551

25482552
_LIBCPP_END_NAMESPACE_STD

libcxx/include/unordered_set

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,8 @@ struct __container_traits<unordered_set<_Value, _Hash, _Pred, _Alloc> > {
11961196
// inserting a single element, the insertion has no effect.
11971197
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
11981198
__is_nothrow_invocable_v<_Hash, const _Value&>;
1199+
1200+
static _LIBCPP_CONSTEXPR const bool __reservable = true;
11991201
};
12001202

12011203
template <class _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
@@ -1816,6 +1818,8 @@ struct __container_traits<unordered_multiset<_Value, _Hash, _Pred, _Alloc> > {
18161818
// inserting a single element, the insertion has no effect.
18171819
static _LIBCPP_CONSTEXPR const bool __emplacement_has_strong_exception_safety_guarantee =
18181820
__is_nothrow_invocable_v<_Hash, const _Value&>;
1821+
1822+
static _LIBCPP_CONSTEXPR const bool __reservable = true;
18191823
};
18201824

18211825
_LIBCPP_END_NAMESPACE_STD

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.modifiers/insert_iter_iter.pass.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// template <class InputIterator>
1414
// void insert(InputIterator first, InputIterator last);
1515

16+
#include <algorithm>
1617
#include <flat_map>
1718
#include <cassert>
1819
#include <functional>
@@ -75,6 +76,7 @@ void test() {
7576
M expected2{{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}};
7677
assert(m == expected2);
7778
}
79+
7880
int main(int, char**) {
7981
test<std::vector<int>, std::vector<double>>();
8082
test<std::deque<int>, std::vector<double>>();
@@ -85,5 +87,12 @@ int main(int, char**) {
8587
auto insert_func = [](auto& m, const auto& newValues) { m.insert(newValues.begin(), newValues.end()); };
8688
test_insert_range_exception_guarantee(insert_func);
8789
}
90+
{
91+
std::flat_map<int, int, std::less<int>, SillyReserveVector<int>, SillyReserveVector<int>> m{{1, 1}, {2, 2}};
92+
std::vector<std::pair<int, int>> v{{3, 3}, {4, 4}};
93+
m.insert(v.begin(), v.end());
94+
assert(std::ranges::equal(m, std::vector<std::pair<int, int>>{{1, 1}, {2, 2}, {3, 3}, {4, 4}}));
95+
}
96+
8897
return 0;
8998
}

libcxx/test/std/containers/container.adaptors/flat.multimap/flat.multimap.modifiers/insert_iter_iter.pass.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// void insert(InputIterator first, InputIterator last);
1717

1818
#include <flat_map>
19+
#include <algorithm>
1920
#include <cassert>
2021
#include <functional>
2122
#include <deque>
@@ -105,5 +106,11 @@ int main(int, char**) {
105106
auto insert_func = [](auto& m, const auto& newValues) { m.insert(newValues.begin(), newValues.end()); };
106107
test_insert_range_exception_guarantee(insert_func);
107108
}
109+
{
110+
std::flat_multimap<int, int, std::less<int>, SillyReserveVector<int>, SillyReserveVector<int>> m{{1, 1}, {2, 2}};
111+
std::vector<std::pair<int, int>> v{{3, 3}, {4, 4}};
112+
m.insert(v.begin(), v.end());
113+
assert(std::ranges::equal(m, std::vector<std::pair<int, int>>{{1, 1}, {2, 2}, {3, 3}, {4, 4}}));
114+
}
108115
return 0;
109116
}

libcxx/test/std/containers/container.adaptors/flat.multiset/flat.multiset.modifiers/insert_iter_iter.pass.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// void insert(InputIterator first, InputIterator last);
1515

1616
#include <flat_set>
17+
#include <algorithm>
1718
#include <cassert>
1819
#include <functional>
1920
#include <deque>
@@ -79,6 +80,12 @@ void test() {
7980
test_one<std::deque<int>>();
8081
test_one<MinSequenceContainer<int>>();
8182
test_one<std::vector<int, min_allocator<int>>>();
83+
{
84+
std::flat_multiset<int, std::less<int>, SillyReserveVector<int>> m{1, 2};
85+
std::vector<int> v{3, 4};
86+
m.insert(v.begin(), v.end());
87+
assert(std::ranges::equal(m, std::vector<int>{1, 2, 3, 4}));
88+
}
8289
}
8390

8491
void test_exception() {

libcxx/test/std/containers/container.adaptors/flat.set/flat.set.modifiers/insert_iter_iter.pass.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// void insert(InputIterator first, InputIterator last);
1515

1616
#include <flat_set>
17+
#include <algorithm>
1718
#include <cassert>
1819
#include <functional>
1920
#include <deque>
@@ -79,6 +80,12 @@ void test() {
7980
test_one<std::deque<int>>();
8081
test_one<MinSequenceContainer<int>>();
8182
test_one<std::vector<int, min_allocator<int>>>();
83+
{
84+
std::flat_set<int, std::less<int>, SillyReserveVector<int>> m{1, 2};
85+
std::vector<int> v{3, 4};
86+
m.insert(v.begin(), v.end());
87+
assert(std::ranges::equal(m, std::vector<int>{1, 2, 3, 4}));
88+
}
8289
}
8390

8491
void test_exception() {

libcxx/test/std/containers/container.adaptors/flat_helpers.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ struct CopyOnlyVector : std::vector<T> {
2525
CopyOnlyVector& operator=(CopyOnlyVector& other) { return this->operator=(other); }
2626
};
2727

28+
template <class T>
29+
struct SillyReserveVector : std::vector<T> {
30+
using std::vector<T>::vector;
31+
32+
void reserve(size_t) { this->clear(); }
33+
};
34+
2835
template <class T, bool ConvertibleToT = false>
2936
struct Transparent {
3037
T t;

0 commit comments

Comments
 (0)