diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index daf9b8f6..f4874d57 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -3618,9 +3618,9 @@ namespace boost { } } - // Find the node before the key, so that it can be erased. - link_pointer find_previous_node( - const_key_type& k, std::size_t bucket_index) + template + link_pointer find_previous_node_impl( + KeyEqual const& eq, Key const& k, std::size_t const bucket_index) { link_pointer prev = this->get_previous_start(bucket_index); if (!prev) { @@ -3631,10 +3631,14 @@ namespace boost { node_pointer n = next_node(prev); if (!n) { return link_pointer(); - } else if (n->is_first_in_group()) { + } + // the `first_in_group()` checks are required for the multi-containers + // for the unique containers, this condition seems to be always true + // + else if (n->is_first_in_group()) { if (node_bucket(n) != bucket_index) { return link_pointer(); - } else if (this->key_eq()(k, this->get_key(n))) { + } else if (eq(k, this->get_key(n))) { return prev; } } @@ -3642,6 +3646,13 @@ namespace boost { } } + // Find the node before the key, so that it can be erased. + link_pointer find_previous_node( + const_key_type& k, std::size_t bucket_index) + { + return find_previous_node_impl(this->key_eq(), k, bucket_index); + } + // Extract and erase inline node_pointer extract_by_key(const_key_type& k) @@ -4047,24 +4058,36 @@ namespace boost { // // no throw - std::size_t erase_key_unique(const_key_type& k) + template + std::size_t erase_key_unique_impl(KeyEqual const& eq, Key const& k) { if (!this->size_) return 0; - std::size_t key_hash = this->hash(k); + + std::size_t key_hash = policy::apply_hash(this->hash_function(), k); std::size_t bucket_index = this->hash_to_bucket(key_hash); - link_pointer prev = this->find_previous_node(k, bucket_index); + + link_pointer prev = + this->find_previous_node_impl(eq, k, bucket_index); + if (!prev) return 0; + node_pointer n = next_node(prev); node_pointer n2 = next_node(n); prev->next_ = n2; --size_; this->fix_bucket(bucket_index, prev, n2); this->destroy_node(n); + return 1; } + std::size_t erase_key_unique(const_key_type& k) + { + return this->erase_key_unique_impl(this->key_eq(), k); + } + void erase_nodes_unique(node_pointer i, node_pointer j) { std::size_t bucket_index = this->node_bucket(i); diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 17dc987d..1577797d 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) @@ -708,6 +709,20 @@ namespace boost { iterator erase(const_iterator); size_type erase(const key_type&); iterator erase(const_iterator, const_iterator); + + template + typename boost::enable_if_c< + detail::is_transparent::value && + detail::is_transparent::value && + !boost::is_convertible::value && + !boost::is_convertible::value, + size_type>::type + erase(BOOST_FWD_REF(Key) k) + { + return table_.erase_key_unique_impl( + this->key_eq(), boost::forward(k)); + } + BOOST_UNORDERED_DEPRECATED("Use erase instead") void quick_erase(const_iterator it) { erase(it); } BOOST_UNORDERED_DEPRECATED("Use erase instead") @@ -1382,6 +1397,7 @@ namespace boost { iterator erase(const_iterator); size_type erase(const key_type&); iterator erase(const_iterator, const_iterator); + BOOST_UNORDERED_DEPRECATED("Use erase instead") void quick_erase(const_iterator it) { erase(it); } BOOST_UNORDERED_DEPRECATED("Use erase instead") @@ -1782,7 +1798,7 @@ namespace boost { typename unordered_map::size_type unordered_map::erase(const key_type& k) { - return table_.erase_key_unique(k); + return table_.erase_key_unique_impl(this->key_eq(), k); } template diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index 7acab1a4..bbcb240b 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -48,6 +48,11 @@ struct transparent_key_equal was_called_ = true; return k1.x_ == x; } + bool operator()(key const& k1, int const x) const + { + was_called_ = true; + return k1.x_ == x; + } }; bool transparent_key_equal::was_called_; @@ -515,6 +520,106 @@ template void test_non_transparent_equal_range() } } +template struct convertible_to_iterator +{ + operator typename UnorderedMap::iterator() + { + return typename UnorderedMap::iterator(); + } +}; + +typedef boost::unordered_map + transparent_unordered_map; + +// test that in the presence of the member function template `erase()`, we still +// invoke the correct iterator overloads when the type is implicitly convertible +// +transparent_unordered_map::iterator erase_overload_compile_test() +{ + convertible_to_iterator c; + transparent_unordered_map map; + transparent_unordered_map::iterator pos = map.begin(); + pos = c; + return map.erase(c); +} + +template void test_transparent_erase() +{ + count_reset(); + + UnorderedMap map; + + unsigned long num_erased = 0; + + num_erased = map.erase(0); + BOOST_TEST(map.empty()); + BOOST_TEST_EQ(num_erased, 0); + BOOST_TEST_EQ(key::count_, 0); + + map[key(0)] = 1337; + map[key(1)] = 1338; + map[key(2)] = 1339; + + BOOST_TEST_EQ(map.size(), 3); + BOOST_TEST(map.find(0) != map.end()); + + int const expected_key_count = static_cast(2 * map.size()); + BOOST_TEST_EQ(key::count_, expected_key_count); + + num_erased = map.erase(0); + BOOST_TEST_EQ(num_erased, 1); + BOOST_TEST_EQ(map.size(), 2); + BOOST_TEST(map.find(0) == map.end()); + + num_erased = map.erase(1337); + BOOST_TEST_EQ(num_erased, 0); + BOOST_TEST_EQ(map.size(), 2); + + BOOST_TEST_EQ(key::count_, expected_key_count); +} + +template void test_non_transparent_erase() +{ + count_reset(); + + UnorderedMap map; + + unsigned long num_erased = 0; + + num_erased = map.erase(0); + BOOST_TEST(map.empty()); + BOOST_TEST_EQ(num_erased, 0); + BOOST_TEST_EQ(key::count_, 1); + + map[key(0)] = 1337; + map[key(1)] = 1338; + map[key(2)] = 1339; + + BOOST_TEST_EQ(map.size(), 3); + BOOST_TEST(map.find(0) != map.end()); + + int key_count = 2 + static_cast(2 * map.size()); + BOOST_TEST_EQ(key::count_, key_count); + + num_erased = map.erase(0); + ++key_count; + BOOST_TEST_EQ(key::count_, key_count); + BOOST_TEST_EQ(num_erased, 1); + BOOST_TEST_EQ(map.size(), 2); + + BOOST_TEST(map.find(0) == map.end()); + ++key_count; + + BOOST_TEST_EQ(key::count_, key_count); + + num_erased = map.erase(1337); + ++key_count; + BOOST_TEST_EQ(num_erased, 0); + BOOST_TEST_EQ(map.size(), 2); + BOOST_TEST_EQ(key::count_, key_count); +} + UNORDERED_AUTO_TEST (unordered_map_transparent_count) { { typedef boost::unordered_map(); test_transparent_find(); test_transparent_equal_range(); + test_transparent_erase(); } { @@ -534,6 +640,7 @@ UNORDERED_AUTO_TEST (unordered_map_transparent_count) { test_non_transparent_count(); test_non_transparent_find(); test_non_transparent_equal_range(); + test_non_transparent_erase(); } { @@ -545,6 +652,7 @@ UNORDERED_AUTO_TEST (unordered_map_transparent_count) { test_non_transparent_count(); test_non_transparent_find(); test_non_transparent_equal_range(); + test_non_transparent_erase(); } { @@ -556,6 +664,7 @@ UNORDERED_AUTO_TEST (unordered_map_transparent_count) { test_non_transparent_count(); test_non_transparent_find(); test_non_transparent_equal_range(); + test_non_transparent_erase(); } }