diff --git a/include/sg14/flat_map.h b/include/sg14/flat_map.h index f9c661e..9ed7c5c 100644 --- a/include/sg14/flat_map.h +++ b/include/sg14/flat_map.h @@ -725,15 +725,63 @@ class flat_map { } } - // TODO: use the hint, here + // TODO: use more of the hint, here template - iterator try_emplace(const_iterator, const Key& k, Args&&... args) { + iterator try_emplace(const_iterator hint, const Key& k, Args&&... args) { + if (empty()) { + // hint must point end() + c_.keys.emplace_back(k); + // TODO: we must make this exception-safe if the container throws + c_.values.emplace_back(static_cast(args)...); + return end() - 1; + } + + // at least 1 element in the map + + if (hint.private_impl_getkey() == c_.keys.end()) { + // check if the current last element should be ordered before k + // if yes, insert at end, else fallback to regular try_emplace because the hint is wrong + auto prev = std::prev(hint); + if (compare_(*prev.private_impl_getkey(), k)) { + c_.keys.emplace_back(k); + // TODO: we must make this exception-safe if the container throws + c_.values.emplace_back(static_cast(args)...); + return end() - 1; + } else { + // hint is wrong, fallback to un-hinted implementation + return try_emplace(k, static_cast(args)...).first; + } + } return try_emplace(k, static_cast(args)...).first; } - // TODO: use the hint, here + // TODO: use more of the hint, here template - iterator try_emplace(const_iterator, Key&& k, Args&&... args) { + iterator try_emplace(const_iterator hint, Key&& k, Args&&... args) { + if (empty()) { + // hint must point end() + c_.keys.emplace_back(static_cast(k)); + // TODO: we must make this exception-safe if the container throws + c_.values.emplace_back(static_cast(args)...); + return end() - 1; + } + + // at least 1 element in the map + + if (hint.private_impl_getkey() == c_.keys.end()) { + // check if the current last element should be ordered before k + // if yes, insert at end, else fallback to regular try_emplace because the hint is wrong + auto prev = std::prev(hint); + if (compare_(*prev.private_impl_getkey(), k)) { + c_.keys.emplace_back(static_cast(k)); + // TODO: we must make this exception-safe if the container throws + c_.values.emplace_back(static_cast(args)...); + return end() - 1; + } else { + // hint is wrong, fallback to un-hinted implementation + return try_emplace(static_cast(k), static_cast(args)...).first; + } + } return try_emplace(static_cast(k), static_cast(args)...).first; } diff --git a/test/flat_map_test.cpp b/test/flat_map_test.cpp index edc6ae4..24a7035 100644 --- a/test/flat_map_test.cpp +++ b/test/flat_map_test.cpp @@ -226,6 +226,117 @@ TEST(flat_map, TryEmplace) } } + +TEST(flat_map, TryEmplaceHint) +{ + sg14::flat_map fm; + sg14::flat_map::iterator iterator; + if (true) { + // try_emplace for a non-existent key does move-from. + InstrumentedWidget w("abc"); + iterator = fm.try_emplace(fm.end(), 1, std::move(w)); + assert(w.is_moved_from); + assert(iterator == fm.begin()); + } + if (true) { + // try_emplace over an existing key is a no-op. + InstrumentedWidget w("def"); + iterator = fm.try_emplace(fm.end(), 1, std::move(w)); + assert(!w.is_moved_from); + assert(fm.size() == 1); + assert(iterator->first == 1); + assert(iterator->second.str() == "abc"); + } + if (true) { + // try_emplace to non-empty with correct hint. + InstrumentedWidget w("def"); + iterator = fm.try_emplace(fm.end(), 2, std::move(w)); + assert(w.is_moved_from); + assert(iterator == (fm.end() - 1)); + assert(fm.size() == 2); + assert(iterator->first == 2); + assert(iterator->second.str() == "def"); + } + if (true) { + // try_emplace to non-empty with wrong hint. + InstrumentedWidget w("qqq"); + iterator = fm.try_emplace(fm.end(), 0, std::move(w)); + assert(w.is_moved_from); + assert(iterator == fm.begin()); + assert(fm.size() == 3); + assert(iterator->first == 0); + assert(iterator->second.str() == "qqq"); + } + if (true) { + // try_emplace to non-empty with wrong hint (not end()). + InstrumentedWidget w("qqq2"); + iterator = fm.try_emplace(fm.end() - 1, -1, std::move(w)); + assert(w.is_moved_from); + assert(iterator == fm.begin()); + assert(fm.size() == 4); + assert(iterator->first == -1); + assert(iterator->second.str() == "qqq2"); + } +} + +TEST(flat_map, TryEmplaceHintConstRef) +{ + sg14::flat_map fm; + sg14::flat_map::iterator iterator; + int key; + if (true) { + // try_emplace for a non-existent key does move-from. + InstrumentedWidget w("abc"); + key = 1; + iterator = fm.try_emplace(fm.end(), key, std::move(w)); + assert(w.is_moved_from); + assert(iterator == fm.begin()); + } + if (true) { + // try_emplace over an existing key is a no-op. + InstrumentedWidget w("def"); + iterator = fm.try_emplace(fm.end(), key, std::move(w)); + assert(!w.is_moved_from); + assert(fm.size() == 1); + assert(iterator->first == 1); + assert(iterator->second.str() == "abc"); + } + if (true) { + // try_emplace to non-empty with correct hint. + InstrumentedWidget w("def"); + key = 2; + iterator = fm.try_emplace(fm.end(), key, std::move(w)); + assert(w.is_moved_from); + assert(iterator == (fm.end() - 1)); + assert(fm.size() == 2); + assert(iterator->first == 2); + assert(iterator->second.str() == "def"); + } + if (true) { + // try_emplace to non-empty with wrong hint. + InstrumentedWidget w("qqq"); + key = 0; + iterator = fm.try_emplace(fm.end(), key, std::move(w)); + assert(w.is_moved_from); + assert(iterator == fm.begin()); + assert(fm.size() == 3); + assert(iterator->first == 0); + assert(iterator->second.str() == "qqq"); + } + if (true) { + // try_emplace to non-empty with wrong hint (not end()). + InstrumentedWidget w("qqq2"); + key = -1; + iterator = fm.try_emplace(fm.end() - 1, key, std::move(w)); + assert(w.is_moved_from); + assert(iterator == fm.begin()); + assert(fm.size() == 4); + assert(iterator->first == -1); + assert(iterator->second.str() == "qqq2"); + } +} + + TEST(flat_map, VectorBool) { using FM = sg14::flat_map;