From 52907f6308cbbcf6a01836a1ad7397b97afc72d3 Mon Sep 17 00:00:00 2001 From: luka Date: Sat, 5 Sep 2026 13:11:29 +0800 Subject: [PATCH] : Unify _Emplace_hint() with Rewrite flat_map's `_Emplace_hint()` to follow the same code pattern as flat_set's: first correct the hint towards `lower_bound(_Key_val)` (for unique containers) or the closest valid insertion position (for non-unique containers), then handle the equivalent-element case (overwriting when `_OverwriteIfExists`), and finally insert through `_Emplace_exact()`. This replaces the previous `weak_ordering` classification logic and removes a duplicated `_IsUnique` special-casing for the overwrite path. Also add flat_map regression coverage for the backward-search hint path, where the hint is positioned after the key: inserting a new key, duplicate try_emplace, duplicate insert, and insert_or_assign overwriting an existing key. Semantics are unchanged for all hint cases. Fixes #6069 --- stl/inc/flat_map | 79 +++++++++++------------ tests/std/tests/P0429R9_flat_map/test.cpp | 36 +++++++++++ 2 files changed, 75 insertions(+), 40 deletions(-) diff --git a/stl/inc/flat_map b/stl/inc/flat_map index fa30f1f3d38..3a6f94ec03b 100644 --- a/stl/inc/flat_map +++ b/stl/inc/flat_map @@ -920,56 +920,55 @@ protected: const const_iterator _Begin = cbegin(); const const_iterator _End = cend(); + const_iterator _Where = _Position; - const weak_ordering _Hint_order = [&] { - if constexpr (_IsUnique) { - if (_Position == _End || _Compare_keys(_Key_val, *_Position._Key_it)) { - if (_Position == _Begin || _Compare_keys(*(_Position._Key_it - 1), _Key_val)) { - return weak_ordering::equivalent; - } else { - return weak_ordering::greater; - } + if constexpr (_IsUnique) { + // look for lower_bound(_Key_val) + if (_Where == _End || !_Compare_keys(*_Where._Key_it, _Key_val)) { + // _Key_val <= *_Where + if (_Where == _Begin || _Compare_keys(*(_Where - 1)._Key_it, _Key_val)) { + // _Key_val > *(_Where-1) ~ lower_bound is _Where + } else { + // _Key_val <= *(_Where-1) ~ lower_bound is in [_Begin,_Where-1] + _Where = _Iterator_from_key_iterator( + _STD lower_bound(_Data.keys.cbegin(), _Where._Key_it - 1, _Key_val, _Pass_key_comp())); } } else { - if (_Position == _End || !_Compare_keys(*_Position._Key_it, _Key_val)) { - if (_Position == _Begin || !_Compare_keys(_Key_val, *(_Position._Key_it - 1))) { - return weak_ordering::equivalent; - } else { - return weak_ordering::greater; - } - } + // _Key_val > *_Where ~ lower_bound is in [_Where+1,_End] + _Where = _Iterator_from_key_iterator( + _STD lower_bound(_Where._Key_it + 1, _Data.keys.cend(), _Key_val, _Pass_key_comp())); } - return weak_ordering::less; - }(); - const auto _New_position = _Iterator_from_key_iterator( - _Hint_order == weak_ordering::equivalent ? _Position._Key_it - : _Hint_order == weak_ordering::less - ? _STD lower_bound(_Position._Key_it, _Data.keys.cend(), _Key_val, _Pass_key_comp()) - : _STD upper_bound(_Data.keys.cbegin(), _Position._Key_it, _Key_val, _Pass_key_comp())); - - if constexpr (_IsUnique) { - if (_Hint_order == weak_ordering::less) { - if (_New_position != _End && !_Compare_keys(_Key_val, *_New_position._Key_it)) { - if constexpr (_OverwriteIfExists) { - *_New_position._Mapped_it = mapped_type(_STD forward<_MappedArgTypes>(_Mapped_args)...); - } - return _New_position; + if (_Where != _End && !_Compare_keys(_Key_val, *_Where._Key_it)) { + const auto _Dist = _Where - cbegin(); + if constexpr (_OverwriteIfExists) { + *(begin() + _Dist)._Mapped_it = mapped_type(_STD forward<_MappedArgTypes>(_Mapped_args)...); } - } else if (_Hint_order == weak_ordering::greater) { - if (_New_position != _Begin && !_Compare_keys(*(_New_position._Key_it - 1), _Key_val)) { - const auto _It = _New_position - 1; - if constexpr (_OverwriteIfExists) { - *_It._Mapped_it = mapped_type(_STD forward<_MappedArgTypes>(_Mapped_args)...); - } - return _It; + return begin() + _Dist; + } + } else { + // look for closest position just prior to _Where, respecting ordering + if (_Where == _End || _Compare_keys(_Key_val, *_Where._Key_it)) { + // _Key_val < *_Where + if (_Where == _Begin || !_Compare_keys(_Key_val, *(_Where - 1)._Key_it)) { + // _Key_val >= *(_Where-1) ~ closest valid position is _Where + } else { + // _Key_val < *(_Where-1) ~ closest valid position is upper_bound(_Key_val) located in + // [_Begin,_Where-1] + _Where = _Iterator_from_key_iterator( + _STD upper_bound(_Data.keys.cbegin(), _Where._Key_it - 1, _Key_val, _Pass_key_comp())); } + // _Key_val < *_Where, so upper_bound is indeed "as close as possible" + } else { + // _Key_val >= *_Where ~ search for lower_bound in [_Where,_End] to place _Key_val "as close as + // possible" + _Where = _Iterator_from_key_iterator( + _STD lower_bound(_Where._Key_it, _Data.keys.cend(), _Key_val, _Pass_key_comp())); } } - const auto _Dist = _New_position - begin(); - _Emplace_exact( - _New_position, _STD forward<_OtherKey>(_Key_val), _STD forward<_MappedArgTypes>(_Mapped_args)...); + const auto _Dist = _Where - cbegin(); + _Emplace_exact(_Where, _STD forward<_OtherKey>(_Key_val), _STD forward<_MappedArgTypes>(_Mapped_args)...); return begin() + _Dist; } diff --git a/tests/std/tests/P0429R9_flat_map/test.cpp b/tests/std/tests/P0429R9_flat_map/test.cpp index ab7a5dc93b9..ca26d1b328d 100644 --- a/tests/std/tests/P0429R9_flat_map/test.cpp +++ b/tests/std/tests/P0429R9_flat_map/test.cpp @@ -1200,6 +1200,41 @@ void test_insert_or_assign() { assert(check_value_content(direct_fm, {direct_init_only(direct_init_only::src_type{42u})})); } +// GH-6069: exercise the backward-search path of hinted insertion, where the hint is positioned after the key +void test_gh_6069() { + flat_map fm{{10, 'm'}, {20, 'o'}, {70, 'e'}, {90, 'w'}}; + + // hint is after the key, key not yet present: insert at the correct position + const auto ins = fm.try_emplace(fm.find(70), 15, 'a'); + assert(ins->first == 15); + assert(ins->second == 'a'); + assert(check_key_content(fm, {10, 15, 20, 70, 90})); + assert(check_value_content(fm, {'m', 'a', 'o', 'e', 'w'})); + + // duplicate key after the hint: try_emplace returns the existing element without inserting + const auto dup = fm.try_emplace(fm.find(70), 20, 'z'); + assert(dup->first == 20); + assert(dup->second == 'o'); + assert(fm.size() == 5); + assert(check_key_content(fm, {10, 15, 20, 70, 90})); + assert(check_value_content(fm, {'m', 'a', 'o', 'e', 'w'})); + + // duplicate key after the hint: insert returns the existing element without inserting + pair dup_pair{20, 'q'}; + const auto dup_ins = fm.insert(fm.find(90), move(dup_pair)); + assert(dup_ins->first == 20); + assert(dup_ins->second == 'o'); + assert(check_key_content(fm, {10, 15, 20, 70, 90})); + assert(check_value_content(fm, {'m', 'a', 'o', 'e', 'w'})); + + // duplicate key after the hint: insert_or_assign overwrites the mapped value + const auto ovr = fm.insert_or_assign(fm.find(90), 15, 'q'); + assert(ovr->first == 15); + assert(ovr->second == 'q'); + assert(check_key_content(fm, {10, 15, 20, 70, 90})); + assert(check_value_content(fm, {'m', 'q', 'o', 'e', 'w'})); +} + void test_comparison() { { flat_map fm1{{1, '1'}, {2, '7'}, {3, '2'}, {4, '9'}}; @@ -1538,6 +1573,7 @@ void run_normal_tests() { test_insert_range(); test_gh_4344(); test_insert_or_assign(); + test_gh_6069(); test_comparison(); test_map_operations_transparent();