Skip to content

[libc++] Do not call reserve in flat containers if underlying container is user defined #140379

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions libcxx/include/__flat_map/flat_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -1017,11 +1017,11 @@ class flat_map {
}

_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);
}
}
Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/__flat_map/flat_multimap.h
Original file line number Diff line number Diff line change
Expand Up @@ -826,11 +826,11 @@ class flat_multimap {
}

_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);
}
}
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__flat_set/flat_multiset.h
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ class flat_multiset {
}

_LIBCPP_HIDE_FROM_ABI void __reserve(size_t __size) {
if constexpr (requires { __keys_.reserve(__size); }) {
if constexpr (__container_traits<_KeyContainer>::__reservable) {
__keys_.reserve(__size);
}
}
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__flat_set/flat_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ class flat_set {
}

_LIBCPP_HIDE_FROM_ABI void __reserve(size_t __size) {
if constexpr (requires { __keys_.reserve(__size); }) {
if constexpr (__container_traits<_KeyContainer>::__reservable) {
__keys_.reserve(__size);
}
}
Expand Down
3 changes: 3 additions & 0 deletions libcxx/include/__type_traits/container_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/__vector/container_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ struct __container_traits<vector<_Tp, _Allocator> > {
// 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
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/deque
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,8 @@ struct __container_traits<deque<_Tp, _Allocator> > {
// 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
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/forward_list
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,8 @@ struct __container_traits<forward_list<_Tp, _Allocator> > {
// - 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
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/list
Original file line number Diff line number Diff line change
Expand Up @@ -1718,6 +1718,8 @@ struct __container_traits<list<_Tp, _Allocator> > {
// - 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
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/map
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,8 @@ struct __container_traits<map<_Key, _Tp, _Compare, _Allocator> > {
// 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 <class _Key, class _Tp, class _Compare, class _Allocator>
Expand Down Expand Up @@ -2083,6 +2085,8 @@ struct __container_traits<multimap<_Key, _Tp, _Compare, _Allocator> > {
// 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
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/set
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,8 @@ struct __container_traits<set<_Key, _Compare, _Allocator> > {
// 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 <class _Key, class _Compare, class _Allocator>
Expand Down Expand Up @@ -1499,6 +1501,8 @@ struct __container_traits<multiset<_Key, _Compare, _Allocator> > {
// 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
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/unordered_map
Original file line number Diff line number Diff line change
Expand Up @@ -1843,6 +1843,8 @@ struct __container_traits<unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc> > {
// 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 <class _Key,
Expand Down Expand Up @@ -2543,6 +2545,8 @@ struct __container_traits<unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc> >
// 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
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/unordered_set
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,8 @@ struct __container_traits<unordered_set<_Value, _Hash, _Pred, _Alloc> > {
// 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 _Value, class _Hash = hash<_Value>, class _Pred = equal_to<_Value>, class _Alloc = allocator<_Value> >
Expand Down Expand Up @@ -1816,6 +1818,8 @@ struct __container_traits<unordered_multiset<_Value, _Hash, _Pred, _Alloc> > {
// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// template <class InputIterator>
// void insert(InputIterator first, InputIterator last);

#include <algorithm>
#include <flat_map>
#include <cassert>
#include <functional>
Expand Down Expand Up @@ -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<int>, std::vector<double>>();
test<std::deque<int>, std::vector<double>>();
Expand All @@ -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<int, int, std::less<int>, SillyReserveVector<int>, SillyReserveVector<int>> m{{1, 1}, {2, 2}};
std::vector<std::pair<int, int>> v{{3, 3}, {4, 4}};
m.insert(v.begin(), v.end());
assert(std::ranges::equal(m, std::vector<std::pair<int, int>>{{1, 1}, {2, 2}, {3, 3}, {4, 4}}));
}

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
// void insert(InputIterator first, InputIterator last);

#include <flat_map>
#include <algorithm>
#include <cassert>
#include <functional>
#include <deque>
Expand Down Expand Up @@ -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<int, int, std::less<int>, SillyReserveVector<int>, SillyReserveVector<int>> m{{1, 1}, {2, 2}};
std::vector<std::pair<int, int>> v{{3, 3}, {4, 4}};
m.insert(v.begin(), v.end());
assert(std::ranges::equal(m, std::vector<std::pair<int, int>>{{1, 1}, {2, 2}, {3, 3}, {4, 4}}));
}
return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// void insert(InputIterator first, InputIterator last);

#include <flat_set>
#include <algorithm>
#include <cassert>
#include <functional>
#include <deque>
Expand Down Expand Up @@ -79,6 +80,12 @@ void test() {
test_one<std::deque<int>>();
test_one<MinSequenceContainer<int>>();
test_one<std::vector<int, min_allocator<int>>>();
{
std::flat_multiset<int, std::less<int>, SillyReserveVector<int>> m{1, 2};
std::vector<int> v{3, 4};
m.insert(v.begin(), v.end());
assert(std::ranges::equal(m, std::vector<int>{1, 2, 3, 4}));
}
}

void test_exception() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// void insert(InputIterator first, InputIterator last);

#include <flat_set>
#include <algorithm>
#include <cassert>
#include <functional>
#include <deque>
Expand Down Expand Up @@ -79,6 +80,12 @@ void test() {
test_one<std::deque<int>>();
test_one<MinSequenceContainer<int>>();
test_one<std::vector<int, min_allocator<int>>>();
{
std::flat_set<int, std::less<int>, SillyReserveVector<int>> m{1, 2};
std::vector<int> v{3, 4};
m.insert(v.begin(), v.end());
assert(std::ranges::equal(m, std::vector<int>{1, 2, 3, 4}));
}
}

void test_exception() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ struct CopyOnlyVector : std::vector<T> {
CopyOnlyVector& operator=(CopyOnlyVector& other) { return this->operator=(other); }
};

template <class T>
struct SillyReserveVector : std::vector<T> {
using std::vector<T>::vector;

void reserve(size_t) { this->clear(); }
};

template <class T, bool ConvertibleToT = false>
struct Transparent {
T t;
Expand Down
Loading