Skip to content

Commit f15aba1

Browse files
committed
review comments
1 parent 5210813 commit f15aba1

File tree

3 files changed

+22
-44
lines changed

3 files changed

+22
-44
lines changed

libcxx/docs/ReleaseNotes/20.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Implemented Papers
4343
- P2985R0: A type trait for detecting virtual base classes (`Github <https://github.com/llvm/llvm-project/issues/105432>`__)
4444
- ``std::jthread`` and ``<stop_token>`` are not guarded behind ``-fexperimental-library`` anymore
4545
- P2674R1: A trait for implicit lifetime types (`Github <https://github.com/llvm/llvm-project/issues/105259>`__)
46+
- P0429R9: A Standard ``flat_map`` is partially implemented and ``flat_map`` is provided (`https://github.com/llvm/llvm-project/issues/105190`__)
4647

4748
Improvements and New Features
4849
-----------------------------

libcxx/include/__flat_map/flat_map.h

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,6 @@ class flat_map {
249249

250250
_LIBCPP_HIDE_FROM_ABI flat_map(const flat_map&) = default;
251251

252-
// The copy/move constructors are not specified in the spec, which means they should be defaulted.
253-
// However, the move constructor can potentially leave a moved-from object in an inconsistent
254-
// state if an exception is thrown.
255252
_LIBCPP_HIDE_FROM_ABI flat_map(flat_map&& __other) noexcept(
256253
is_nothrow_move_constructible_v<_KeyContainer> && is_nothrow_move_constructible_v<_MappedContainer> &&
257254
is_nothrow_move_constructible_v<_Compare>)
@@ -491,19 +488,21 @@ class flat_map {
491488
return *this;
492489
}
493490

494-
// copy/move assignment are not specified in the spec (defaulted)
495-
// but move assignment can potentially leave moved from object in an inconsistent
496-
// state if an exception is thrown
497491
_LIBCPP_HIDE_FROM_ABI flat_map& operator=(const flat_map&) = default;
498492

499493
_LIBCPP_HIDE_FROM_ABI flat_map& operator=(flat_map&& __other) noexcept(
500494
is_nothrow_move_assignable_v<_KeyContainer> && is_nothrow_move_assignable_v<_MappedContainer> &&
501495
is_nothrow_move_assignable_v<_Compare>) {
496+
// No matter what happens, we always want to clear the other container before returning
497+
// since we moved from it
502498
auto __clear_other_guard = std::__make_scope_guard([&]() noexcept { __other.clear() /* noexcept */; });
503-
auto __clear_self_guard = std::__make_exception_guard([&]() noexcept { clear() /* noexcept */; });
504-
__containers_ = std::move(__other.__containers_);
505-
__compare_ = std::move(__other.__compare_);
506-
__clear_self_guard.__complete();
499+
{
500+
// If an exception is thrown, we have no choice but to clear *this to preserve invariants
501+
auto __on_exception = std::__make_exception_guard([&]() noexcept { clear() /* noexcept */; });
502+
__containers_ = std::move(__other.__containers_);
503+
__compare_ = std::move(__other.__compare_);
504+
__on_exception.__complete();
505+
}
507506
return *this;
508507
}
509508

@@ -568,15 +567,15 @@ class flat_map {
568567
if (__it == end()) {
569568
std::__throw_out_of_range("flat_map::at(const key_type&): Key does not exist");
570569
}
571-
return (*__it).second;
570+
return __it->second;
572571
}
573572

574573
_LIBCPP_HIDE_FROM_ABI const mapped_type& at(const key_type& __x) const {
575574
auto __it = find(__x);
576575
if (__it == end()) {
577576
std::__throw_out_of_range("flat_map::at(const key_type&) const: Key does not exist");
578577
}
579-
return (*__it).second;
578+
return __it->second;
580579
}
581580

582581
template <class _Kp>
@@ -586,7 +585,7 @@ class flat_map {
586585
if (__it == end()) {
587586
std::__throw_out_of_range("flat_map::at(const K&): Key does not exist");
588587
}
589-
return (*__it).second;
588+
return __it->second;
590589
}
591590

592591
template <class _Kp>
@@ -596,7 +595,7 @@ class flat_map {
596595
if (__it == end()) {
597596
std::__throw_out_of_range("flat_map::at(const K&) const: Key does not exist");
598597
}
599-
return (*__it).second;
598+
return __it->second;
600599
}
601600

602601
// [flat.map.modifiers], modifiers
@@ -805,7 +804,8 @@ class flat_map {
805804
_LIBCPP_HIDE_FROM_ABI void swap(flat_map& __y) noexcept {
806805
// warning: The spec has unconditional noexcept, which means that
807806
// if any of the following functions throw an exception,
808-
// std::terminate will be called
807+
// std::terminate will be called.
808+
// This is discussed in P2767, which hasn't been voted on yet.
809809
ranges::swap(__compare_, __y.__compare_);
810810
ranges::swap(__containers_.keys, __y.__containers_.keys);
811811
ranges::swap(__containers_.values, __y.__containers_.values);
@@ -956,8 +956,13 @@ class flat_map {
956956
return ranges::adjacent_find(__key_container, __greater_or_equal_to) == ranges::end(__key_container);
957957
}
958958

959+
// This function is only used in constructors. So there is not exception handling in this function.
960+
// If the function exits via an exception, there will be no flat_map object constructed, thus, there
961+
// is no invariant state to preserve
959962
_LIBCPP_HIDE_FROM_ABI void __sort_and_unique() {
960963
auto __zv = ranges::views::zip(__containers_.keys, __containers_.values);
964+
// To be consistent with std::map's behaviour, we use stable_sort instead of sort.
965+
// As a result, if there are duplicated keys, the first value in the original order will be taken.
961966
ranges::stable_sort(__zv, __compare_, [](const auto& __p) -> decltype(auto) { return std::get<0>(__p); });
962967
auto __dup_start = ranges::unique(__zv, __key_equiv(__compare_)).begin();
963968
auto __dist = ranges::distance(__zv.begin(), __dup_start);

libcxx/test/std/containers/container.adaptors/flat.map/flat.map.iterators/reverse_iterator.pass.cpp

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -70,35 +70,6 @@ int main(int, char**) {
7070
}
7171
assert(i == m.rbegin());
7272
}
73-
// std::string is not a sequence container
74-
#if 0
75-
{
76-
using M = std::flat_map<short, char, std::less<>, std::deque<short>, std::string>;
77-
const M m = {{1,'a'}, {2,'b'}, {3,'c'}, {4,'d'}};
78-
ASSERT_SAME_TYPE(decltype(m.rbegin()), M::const_reverse_iterator);
79-
ASSERT_SAME_TYPE(decltype(m.crbegin()), M::const_reverse_iterator);
80-
ASSERT_SAME_TYPE(decltype(m.rend()), M::const_reverse_iterator);
81-
ASSERT_SAME_TYPE(decltype(m.crend()), M::const_reverse_iterator);
82-
assert(m.size() == 4);
83-
assert(std::distance(m.rbegin(), m.rend()) == 4);
84-
assert(std::distance(m.crbegin(), m.crend()) == 4);
85-
M::const_reverse_iterator i; // default-construct
86-
ASSERT_SAME_TYPE(decltype(i->first), const short&);
87-
ASSERT_SAME_TYPE(decltype(i->second), const char&);
88-
i = m.rbegin(); // move-assignment
89-
for (int j = 4; j >= 1; --j, ++i) { // pre-increment
90-
assert(i->first == j);
91-
assert(i->second == 'a' + j - 1);
92-
}
93-
assert(i == m.rend());
94-
for (int j = 1; j <= 4; ++j) {
95-
--i; // pre-decrement
96-
assert((*i).first == j);
97-
assert((*i).second == 'a' + j - 1);
98-
}
99-
assert(i == m.rbegin());
100-
}
101-
#endif
10273
{
10374
// N3644 testing
10475
using C = std::flat_map<int, char>;
@@ -114,5 +85,6 @@ int main(int, char**) {
11485
assert(!(ii1 != cii));
11586
assert(!(cii != ii1));
11687
}
88+
11789
return 0;
11890
}

0 commit comments

Comments
 (0)