diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 25f8ff83..60b831cb 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -3704,6 +3704,27 @@ struct table_impl : boost::unordered::detail::table } } + template + emplace_return insert_or_assign_impl( + BOOST_FWD_REF(Key) k, BOOST_FWD_REF(M) obj) + { + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + + if (pos) { + pos->value().second = boost::forward(obj); + return emplace_return(iterator(pos), false); + } else { + return emplace_return( + iterator(this->resize_and_add_node( + boost::unordered::detail::func::construct_node_pair( + this->node_alloc(), boost::forward(k), + boost::forward(obj)), + key_hash)), + true); + } + } + //////////////////////////////////////////////////////////////////////// // Insert range methods // diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 3b842d54..bb3d5884 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -342,6 +342,37 @@ template class unordered_map return this->emplace_hint(hint, boost::move(x)); } + template + std::pair insert_or_assign( + key_type const& k, BOOST_FWD_REF(M) obj) + { + return table_.insert_or_assign_impl(k, boost::forward(obj)); + } + + template + iterator insert_or_assign( + const_iterator, key_type const& k, BOOST_FWD_REF(M) obj) + { + return table_.insert_or_assign_impl(k, boost::forward(obj)).first; + } + + template + std::pair insert_or_assign( + BOOST_RV_REF(key_type) k, BOOST_FWD_REF(M) obj) + { + return table_.insert_or_assign_impl( + boost::move(k), boost::forward(obj)); + } + + template + iterator insert_or_assign( + const_iterator, BOOST_RV_REF(key_type) k, BOOST_FWD_REF(M) obj) + { + return table_ + .insert_or_assign_impl(boost::move(k), boost::forward(obj)) + .first; + } + template void insert(InputIt, InputIt); #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) diff --git a/test/unordered/compile_tests.hpp b/test/unordered/compile_tests.hpp index 6c59860d..457b0238 100644 --- a/test/unordered/compile_tests.hpp +++ b/test/unordered/compile_tests.hpp @@ -301,13 +301,18 @@ template void unordered_equivalent_test(X& r, T const& t) } template -void unordered_map_functions(X&, Key const& k, T const&) +void unordered_map_functions(X&, Key const& k, T const& v) { typedef BOOST_DEDUCED_TYPENAME X::mapped_type mapped_type; + typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; X a; test::check_return_type::equals_ref(a[k]); test::check_return_type::equals_ref(a.at(k)); + test::check_return_type >::equals( + a.insert_or_assign(k, v)); + test::check_return_type::equals( + a.insert_or_assign(a.begin(), k, v)); X const b = a; test::check_return_type::equals_ref(b.at(k)); diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index 1f477ade..86edfe24 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -505,6 +505,138 @@ template void map_tests(X*, test::random_generator generator) test::check_equivalent_keys(x); } +template void map_tests2(X*, test::random_generator generator) +{ + typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + std::cerr << "insert_or_assign\n"; + + { + test::check_instances check_; + + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); + + std::pair r = + x.insert_or_assign(it->first, it->second); + BOOST_TEST(*r.first == *it); + + tracker[it->first] = it->second; + tracker.compare_key(x, *it); + + if (static_cast(x.size()) < + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + tracker.compare(x); + test::check_equivalent_keys(x); + } + + std::cerr << "insert_or_assign(begin)\n"; + + { + test::check_instances check_; + + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); + + iterator r = x.insert_or_assign(x.begin(), it->first, it->second); + BOOST_TEST(*r == *it); + + tracker[it->first] = it->second; + tracker.compare_key(x, *it); + + if (static_cast(x.size()) < + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + tracker.compare(x); + test::check_equivalent_keys(x); + } + + std::cerr << "insert_or_assign(end)\n"; + + { + test::check_instances check_; + + X x; + test::ordered tracker = test::create_ordered(x); + + test::random_values v(1000, generator); + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); + + iterator r = x.insert_or_assign(x.end(), it->first, it->second); + BOOST_TEST(*r == *it); + + tracker[it->first] = it->second; + tracker.compare_key(x, *it); + + if (static_cast(x.size()) < + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + tracker.compare(x); + test::check_equivalent_keys(x); + } + + std::cerr << "insert_or_assign(last)\n"; + + { + test::check_instances check_; + + X x; + test::ordered tracker = test::create_ordered(x); + iterator last = x.begin(); + + test::random_values v(1000, generator); + for (BOOST_DEDUCED_TYPENAME test::random_values::iterator it = + v.begin(); + it != v.end(); ++it) { + BOOST_DEDUCED_TYPENAME X::size_type old_bucket_count = + x.bucket_count(); + float b = x.max_load_factor(); + + iterator r = x.insert_or_assign(last, it->first, it->second); + BOOST_TEST(*r == *it); + + tracker[it->first] = it->second; + tracker.compare_key(x, *it); + + if (static_cast(x.size()) < + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + + last = r; + } + + tracker.compare(x); + test::check_equivalent_keys(x); + } +} + // Some tests for when the range's value type doesn't match the container's // value type. @@ -600,6 +732,9 @@ UNORDERED_TEST(default_emplace_tests, UNORDERED_TEST(map_tests, ((test_map))((default_generator)(generate_collisions)(limited_range))) +UNORDERED_TEST( + map_tests2, ((test_map))((default_generator)(generate_collisions))) + UNORDERED_TEST(map_insert_range_test1, ((test_multimap_std_alloc)(test_map)(test_multimap))( (default_generator)(generate_collisions)(limited_range)))