diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 60b831cb..b80dbed0 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1721,6 +1721,35 @@ construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Mapped) m) BOOST_CATCH_END return a.release(); } + +template +inline typename boost::unordered::detail::allocator_traits::pointer +construct_node_pair_from_args( + Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_UNORDERED_EMPLACE_ARGS) +{ + node_constructor a(alloc); + a.create_node(); + boost::unordered::detail::func::call_construct( + alloc, boost::unordered::detail::func::const_cast_pointer( + boost::addressof(a.node_->value_ptr()->first)), + boost::forward(k)); + BOOST_TRY + { + boost::unordered::detail::func::construct_from_args( + alloc, boost::unordered::detail::func::const_cast_pointer( + boost::addressof(a.node_->value_ptr()->second)), + BOOST_UNORDERED_EMPLACE_FORWARD); + } + BOOST_CATCH(...) + { + boost::unordered::detail::func::call_destroy( + alloc, boost::unordered::detail::func::const_cast_pointer( + boost::addressof(a.node_->value_ptr()->first))); + BOOST_RETHROW; + } + BOOST_CATCH_END + return a.release(); +} } } } @@ -3547,22 +3576,6 @@ struct table_impl : boost::unordered::detail::table return this->add_node(b.release(), key_hash); } - value_type& operator[](const_key_type& k) - { - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - if (pos) { - return pos->value(); - } else { - return this - ->resize_and_add_node( - boost::unordered::detail::func::construct_node_pair( - this->node_alloc(), k), - key_hash) - ->value(); - } - } - #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) emplace_return emplace(boost::unordered::detail::emplace_args1< @@ -3704,6 +3717,64 @@ struct table_impl : boost::unordered::detail::table } } + template + emplace_return try_emplace_impl(BOOST_FWD_REF(Key) k) + { + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + if (pos) { + 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)), + key_hash)), + true); + } + } + + template + iterator try_emplace_hint_impl(c_iterator hint, BOOST_FWD_REF(Key) k) + { + if (hint.node_ && this->key_eq()(hint->first, k)) { + return iterator(hint.node_); + } else { + return try_emplace_impl(k).first; + } + } + + template + emplace_return try_emplace_impl( + BOOST_FWD_REF(Key) k, BOOST_UNORDERED_EMPLACE_ARGS) + { + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + if (pos) { + return emplace_return(iterator(pos), false); + } else { + return emplace_return( + iterator(this->resize_and_add_node( + boost::unordered::detail::func:: + construct_node_pair_from_args(this->node_alloc(), + boost::forward(k), + BOOST_UNORDERED_EMPLACE_FORWARD), + key_hash)), + true); + } + } + + template + iterator try_emplace_hint_impl( + c_iterator hint, BOOST_FWD_REF(Key) k, BOOST_UNORDERED_EMPLACE_ARGS) + { + if (hint.node_ && this->key_eq()(hint->first, k)) { + return iterator(hint.node_); + } else { + return try_emplace_impl(k, BOOST_UNORDERED_EMPLACE_FORWARD).first; + } + } + template emplace_return insert_or_assign_impl( BOOST_FWD_REF(Key) k, BOOST_FWD_REF(M) obj) diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index bb3d5884..dbba089a 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -220,6 +220,37 @@ template class unordered_map { return table_.emplace_hint(hint, boost::forward(args)...); } + + template + std::pair try_emplace( + key_type const& k, BOOST_FWD_REF(Args)... args) + { + return table_.try_emplace_impl(k, boost::forward(args)...); + } + + template + iterator try_emplace( + const_iterator hint, key_type const& k, BOOST_FWD_REF(Args)... args) + { + return table_.try_emplace_hint_impl( + hint, k, boost::forward(args)...); + } + + template + std::pair try_emplace( + BOOST_RV_REF(key_type) k, BOOST_FWD_REF(Args)... args) + { + return table_.try_emplace_impl( + boost::move(k), boost::forward(args)...); + } + + template + iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, + BOOST_FWD_REF(Args)... args) + { + return table_.try_emplace_hint_impl( + hint, boost::move(k), boost::forward(args)...); + } #else #if !BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x5100)) @@ -246,6 +277,18 @@ template class unordered_map #endif + template + std::pair try_emplace(BOOST_FWD_REF(Key) k) + { + return table_.try_emplace_impl(boost::forward(k)); + } + + template + iterator try_emplace(const_iterator hint, BOOST_FWD_REF(Key) k) + { + return table_.try_emplace_hint_impl(hint, boost::forward(k)); + } + template std::pair emplace(BOOST_FWD_REF(A0) a0) { @@ -261,6 +304,39 @@ template class unordered_map boost::forward(a0))); } + template + std::pair try_emplace( + key_type const& k, BOOST_FWD_REF(A0) a0) + { + return table_.try_emplace_impl( + k, boost::unordered::detail::create_emplace_args( + boost::forward(a0))); + } + + template + iterator try_emplace( + const_iterator hint, key_type const& k, BOOST_FWD_REF(A0) a0) + { + return table_.try_emplace_hint_impl( + hint, k, boost::unordered::detail::create_emplace_args( + boost::forward(a0))); + } + + template + std::pair try_emplace( + BOOST_RV_REF(key_type) k, BOOST_FWD_REF(A0) a0) + { + return table_.try_emplace_impl(boost::move(k), boost::forward(a0)); + } + + template + iterator try_emplace( + const_iterator hint, BOOST_RV_REF(key_type) k, BOOST_FWD_REF(A0) a0) + { + return table_.try_emplace_hint_impl( + hint, boost::move(k), boost::forward(a0)); + } + template std::pair emplace( BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) @@ -278,6 +354,44 @@ template class unordered_map boost::forward(a0), boost::forward(a1))); } + template + std::pair try_emplace( + key_type const& k, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.try_emplace_impl( + k, boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + + template + iterator try_emplace(const_iterator hint, key_type const& k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.try_emplace_hint_impl( + hint, k, boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + + template + std::pair try_emplace( + BOOST_RV_REF(key_type) k, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.try_emplace_impl( + boost::move(k), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + + template + iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.try_emplace_hint_impl( + hint, boost::move(k), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + template std::pair emplace( BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) @@ -297,6 +411,49 @@ template class unordered_map boost::forward(a2))); } + template + std::pair try_emplace(key_type const& k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_.try_emplace_impl( + k, boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))); + } + + template + iterator try_emplace(const_iterator hint, key_type const& k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_ + .try_emplace_impl_( + hint, k, boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))) + .first; + } + + template + std::pair try_emplace(BOOST_RV_REF(key_type) k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_.try_emplace_impl( + boost::move(k), boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))); + } + + template + iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_.try_emplace_hint_impl( + hint, boost::move(k), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1), + boost::forward(a2))); + } + #define BOOST_UNORDERED_EMPLACE(z, n, _) \ template \ std::pair emplace( \ @@ -313,6 +470,42 @@ template class unordered_map return table_.emplace_hint( \ hint, boost::unordered::detail::create_emplace_args( \ BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + } \ + \ + template \ + std::pair try_emplace( \ + key_type const& k, BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ + { \ + return table_.try_emplace_impl( \ + k, boost::unordered::detail::create_emplace_args( \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + } \ + \ + template \ + iterator try_emplace(const_iterator hint, key_type const& k, \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ + { \ + return table_.try_emplace_hint_impl(hint, k, \ + boost::unordered::detail::create_emplace_args(BOOST_PP_ENUM_##z( \ + n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + } \ + \ + template \ + std::pair try_emplace(BOOST_RV_REF(key_type) k, \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ + { \ + return table_.try_emplace_impl(boost::move(k), \ + boost::unordered::detail::create_emplace_args(BOOST_PP_ENUM_##z( \ + n, BOOST_UNORDERED_CALL_FORWARD, a))); \ + } \ + \ + template \ + iterator try_emplace(const_iterator hint, BOOST_RV_REF(key_type) k, \ + BOOST_PP_ENUM_##z(n, BOOST_UNORDERED_FWD_PARAM, a)) \ + { \ + return table_.try_emplace_hint_impl(hint, boost::move(k), \ + boost::unordered::detail::create_emplace_args(BOOST_PP_ENUM_##z( \ + n, BOOST_UNORDERED_CALL_FORWARD, a))); \ } BOOST_PP_REPEAT_FROM_TO( @@ -1114,7 +1307,7 @@ template typename unordered_map::mapped_type& unordered_map::operator[](const key_type& k) { - return table_[k].second; + return table_.try_emplace_impl(k).first->second; } template diff --git a/test/unordered/compile_tests.hpp b/test/unordered/compile_tests.hpp index 457b0238..d0d1fd7c 100644 --- a/test/unordered/compile_tests.hpp +++ b/test/unordered/compile_tests.hpp @@ -309,10 +309,21 @@ void unordered_map_functions(X&, Key const& k, T const& v) 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.try_emplace(k, v)); + test::check_return_type >::equals( + a.try_emplace(rvalue(k), v)); + test::check_return_type::equals(a.try_emplace(a.begin(), k, v)); + test::check_return_type::equals( + a.try_emplace(a.begin(), rvalue(k), v)); test::check_return_type >::equals( a.insert_or_assign(k, v)); + test::check_return_type >::equals( + a.insert_or_assign(rvalue(k), v)); test::check_return_type::equals( a.insert_or_assign(a.begin(), k, v)); + test::check_return_type::equals( + a.insert_or_assign(a.begin(), rvalue(k), v)); X const b = a; test::check_return_type::equals_ref(b.at(k)); diff --git a/test/unordered/emplace_tests.cpp b/test/unordered/emplace_tests.cpp index 04c0253b..e86a066f 100644 --- a/test/unordered/emplace_tests.cpp +++ b/test/unordered/emplace_tests.cpp @@ -465,6 +465,55 @@ UNORDERED_AUTO_TEST(emplace_multimap) BOOST_TEST_EQ(check_.instances(), 20); BOOST_TEST_EQ(check_.constructions(), 20); } + +UNORDERED_AUTO_TEST(try_emplace) +{ + test::check_instances check_; + + typedef boost::unordered_map container; + typedef container::iterator iterator; + typedef std::pair return_type; + container x(10); + return_type r1, r2, r3; + + int k1 = 3; + emplace_value m1(414, "grr"); + r1 = x.try_emplace(3, 414, "grr"); + BOOST_TEST(r1.second); + BOOST_TEST(r1.first->first == k1); + BOOST_TEST(r1.first->second == m1); + BOOST_TEST_EQ(x.size(), 1u); + BOOST_TEST_EQ(check_.instances(), 2); + BOOST_TEST_EQ(check_.constructions(), 2); + + int k2 = 10; + emplace_value m2(25, "", 'z'); + r2 = x.try_emplace(10, 25, std::string(""), 'z'); + BOOST_TEST(r2.second); + BOOST_TEST(r2.first->first == k2); + BOOST_TEST(r2.first->second == m2); + BOOST_TEST_EQ(x.size(), 2u); + BOOST_TEST_EQ(check_.instances(), 4); + BOOST_TEST_EQ(check_.constructions(), 4); + + BOOST_TEST(x.find(k1)->second == m1); + BOOST_TEST(x.find(k2)->second == m2); + + r3 = x.try_emplace(k2, 68, "jfeoj", 'p', 49309, 2323); + BOOST_TEST(!r3.second); + BOOST_TEST(r3.first == r2.first); + BOOST_TEST(r3.first->second == m2); + BOOST_TEST_EQ(x.size(), 2u); + BOOST_TEST_EQ(check_.instances(), 4); + BOOST_TEST_EQ(check_.constructions(), 4); + + BOOST_TEST(r2.first == x.try_emplace(r2.first, k2, 808709, "what")); + BOOST_TEST( + r2.first == + x.try_emplace(r2.first, k2, 10, "xxx", 'a', 4, 5, 6, 7, 8, 9, 10)); + BOOST_TEST(r2.first->second == m2); + BOOST_TEST_EQ(x.size(), 2u); +} } RUN_TESTS() diff --git a/test/unordered/insert_tests.cpp b/test/unordered/insert_tests.cpp index 86edfe24..ae50fa18 100644 --- a/test/unordered/insert_tests.cpp +++ b/test/unordered/insert_tests.cpp @@ -637,6 +637,171 @@ template void map_tests2(X*, test::random_generator generator) } } +template void try_emplace_tests(X*, test::random_generator generator) +{ + std::cerr << "try_emplace(key, value)\n"; + + typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + + { + 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 pos = x.find(it->first); + bool found = pos != x.end(); + + std::pair r = + x.try_emplace(it->first, it->second); + if (found) { + BOOST_TEST(pos == r.first); + BOOST_TEST(!r.second); + } else { + BOOST_TEST(r.second); + } + BOOST_TEST_EQ(r.first->first, it->first); + BOOST_TEST_EQ(r.first->second, it->second); + + tracker.insert(*it); + tracker.compare_key(x, *it); + + if (static_cast(x.size()) < + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } + + std::cerr << "try_emplace(begin(), key, value)\n"; + + typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + + { + 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 pos = x.find(it->first); + bool found = pos != x.end(); + + typename X::iterator r = + x.try_emplace(r.begin(), it->first, it->second); + if (found) { + BOOST_TEST(pos == r); + } + BOOST_TEST_EQ(r->first, it->first); + BOOST_TEST_EQ(r->second, it->second); + + tracker.insert(*it); + tracker.compare_key(x, *it); + + if (static_cast(x.size()) < + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } + + std::cerr << "try_emplace(end(), key, value)\n"; + + typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + + { + 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 pos = x.find(it->first); + bool found = pos != x.end(); + + typename X::iterator r = + x.try_emplace(r.end(), it->first, it->second); + if (found) { + BOOST_TEST(pos == r); + } + BOOST_TEST_EQ(r->first, it->first); + BOOST_TEST_EQ(r->second, it->second); + + tracker.insert(*it); + tracker.compare_key(x, *it); + + if (static_cast(x.size()) < + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } + + std::cerr << "try_emplace(pos, key, value)\n"; + + typedef BOOST_DEDUCED_TYPENAME X::iterator iterator; + + { + 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 pos = x.find(it->first); + bool found = pos != x.end(); + + typename X::iterator r = x.try_emplace(pos, it->first, it->second); + if (found) { + BOOST_TEST(pos == r); + } + BOOST_TEST_EQ(r->first, it->first); + BOOST_TEST_EQ(r->second, it->second); + + tracker.insert(*it); + tracker.compare_key(x, *it); + + if (static_cast(x.size()) < + b * static_cast(old_bucket_count)) + BOOST_TEST(x.bucket_count() == old_bucket_count); + } + + test::check_equivalent_keys(x); + } +} + // Some tests for when the range's value type doesn't match the container's // value type. @@ -876,6 +1041,10 @@ UNORDERED_AUTO_TEST(map_emplace_test) x.emplace(2, 3); BOOST_TEST( x.find(2) != x.end() && x.find(2)->second == overloaded_constructor(3)); + + x.try_emplace(5); + BOOST_TEST( + x.find(5) != x.end() && x.find(5)->second == overloaded_constructor()); } UNORDERED_AUTO_TEST(set_emplace_test) @@ -954,6 +1123,23 @@ UNORDERED_AUTO_TEST(map_emplace_test2) BOOST_TEST(x.find(overloaded_constructor(9, 3, 1)) != x.end() && x.find(overloaded_constructor(9, 3, 1))->second == overloaded_constructor(10)); + + x.clear(); + + x.try_emplace(overloaded_constructor()); + BOOST_TEST( + x.find(overloaded_constructor()) != x.end() && + x.find(overloaded_constructor())->second == overloaded_constructor()); + + x.try_emplace(1); + BOOST_TEST( + x.find(overloaded_constructor(1)) != x.end() && + x.find(overloaded_constructor(1))->second == overloaded_constructor()); + + x.try_emplace(overloaded_constructor(2, 3), 4, 5, 6); + BOOST_TEST(x.find(overloaded_constructor(2, 3)) != x.end() && + x.find(overloaded_constructor(2, 3))->second == + overloaded_constructor(4, 5, 6)); } UNORDERED_AUTO_TEST(set_emplace_test2)