Skip to content
Open
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
79 changes: 39 additions & 40 deletions stl/inc/flat_map
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
36 changes: 36 additions & 0 deletions tests/std/tests/P0429R9_flat_map/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, char> 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<int, char> fm1{{1, '1'}, {2, '7'}, {3, '2'}, {4, '9'}};
Expand Down Expand Up @@ -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<flat_map>();
Expand Down