diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index f4874d57..e7bae279 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -3655,14 +3655,15 @@ namespace boost { // Extract and erase - inline node_pointer extract_by_key(const_key_type& k) + template node_pointer extract_by_key_impl(Key const& k) { if (!this->size_) { return node_pointer(); } - 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(this->key_eq(), k, bucket_index); if (!prev) { return node_pointer(); } @@ -3679,6 +3680,11 @@ namespace boost { return n; } + inline node_pointer extract_by_key(const_key_type& k) + { + return extract_by_key_impl(k); + } + // Reserve and rehash void reserve_for_insert(std::size_t); diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 1577797d..a41e1d9c 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -427,7 +427,20 @@ namespace boost { node_type extract(const key_type& k) { - return node_type(table_.extract_by_key(k), table_.node_alloc()); + return node_type(table_.extract_by_key_impl(k), table_.node_alloc()); + } + + template + typename boost::enable_if_c< + detail::is_transparent::value && + detail::is_transparent::value && + !boost::is_convertible::value && + !boost::is_convertible::value, + node_type>::type + extract(BOOST_FWD_REF(Key) k) + { + return node_type(table_.extract_by_key_impl(boost::forward(k)), + table_.node_alloc()); } insert_return_type insert(BOOST_RV_REF(node_type) np) diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index bbcb240b..18cd7200 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -522,9 +522,9 @@ template void test_non_transparent_equal_range() template struct convertible_to_iterator { - operator typename UnorderedMap::iterator() + operator typename UnorderedMap::const_iterator() { - return typename UnorderedMap::iterator(); + return typename UnorderedMap::const_iterator(); } }; @@ -539,7 +539,7 @@ transparent_unordered_map::iterator erase_overload_compile_test() { convertible_to_iterator c; transparent_unordered_map map; - transparent_unordered_map::iterator pos = map.begin(); + transparent_unordered_map::const_iterator pos = map.begin(); pos = c; return map.erase(c); } @@ -620,6 +620,103 @@ template void test_non_transparent_erase() BOOST_TEST_EQ(key::count_, key_count); } +// test that in the presence of the member function template `extract()`, we still +// invoke the correct iterator overloads when the type is implicitly convertible +// +transparent_unordered_map::node_type extract_overload_compile_test() +{ + convertible_to_iterator c; + transparent_unordered_map map; + transparent_unordered_map::const_iterator pos = map.begin(); + pos = c; + return map.extract(c); +} + +template void test_transparent_extract() +{ + typedef typename UnorderedMap::node_type node_type; + typedef typename UnorderedMap::const_iterator const_iterator; + + count_reset(); + + UnorderedMap map; + + node_type nh = map.extract(0); + BOOST_TEST(nh.empty()); + BOOST_TEST(key::count_ == 0); + + map[key(0)] = 1337; + map[key(1)] = 1338; + map[key(2)] = 1339; + BOOST_TEST(map.size() == 3); + + int const expected_key_count = static_cast(2 * map.size()); + BOOST_TEST(key::count_ == expected_key_count); + + nh = map.extract(1); + BOOST_TEST(map.size() == 2); + BOOST_TEST(nh.key().x_ == 1); + BOOST_TEST(nh.mapped() == 1338); + + nh.mapped() = 1340; + + map.insert(boost::move(nh)); + + BOOST_TEST(map.size() == 3); + + const_iterator pos = map.find(1); + BOOST_TEST(pos != map.end()); + BOOST_TEST(pos->second == 1340); + + nh = map.extract(1337); + BOOST_TEST(nh.empty()); + + BOOST_TEST(key::count_ == expected_key_count); +} + +template void test_non_transparent_extract() +{ + typedef typename UnorderedMap::node_type node_type; + typedef typename UnorderedMap::const_iterator const_iterator; + + count_reset(); + + UnorderedMap map; + + node_type nh = map.extract(0); + BOOST_TEST(nh.empty()); + BOOST_TEST(key::count_ == 1); + + map[key(0)] = 1337; + map[key(1)] = 1338; + map[key(2)] = 1339; + BOOST_TEST(map.size() == 3); + + int key_count = 1 + static_cast(2 * map.size()); + BOOST_TEST(key::count_ == key_count); + + nh = map.extract(1); + BOOST_TEST(map.size() == 2); + BOOST_TEST(nh.key().x_ == 1); + BOOST_TEST(nh.mapped() == 1338); + BOOST_TEST(key::count_ == ++key_count); + + nh.mapped() = 1340; + + map.insert(boost::move(nh)); + + BOOST_TEST(map.size() == 3); + + const_iterator pos = map.find(1); + BOOST_TEST(pos != map.end()); + BOOST_TEST(pos->second == 1340); + BOOST_TEST(key::count_ == ++key_count); + + nh = map.extract(1337); + BOOST_TEST(nh.empty()); + BOOST_TEST(key::count_ == ++key_count); +} + UNORDERED_AUTO_TEST (unordered_map_transparent_count) { { typedef boost::unordered_map(); test_transparent_equal_range(); test_transparent_erase(); + test_transparent_extract(); } { @@ -641,6 +739,7 @@ UNORDERED_AUTO_TEST (unordered_map_transparent_count) { test_non_transparent_find(); test_non_transparent_equal_range(); test_non_transparent_erase(); + test_non_transparent_extract(); } { @@ -653,6 +752,7 @@ UNORDERED_AUTO_TEST (unordered_map_transparent_count) { test_non_transparent_find(); test_non_transparent_equal_range(); test_non_transparent_erase(); + test_non_transparent_extract(); } { @@ -665,6 +765,7 @@ UNORDERED_AUTO_TEST (unordered_map_transparent_count) { test_non_transparent_find(); test_non_transparent_equal_range(); test_non_transparent_erase(); + test_non_transparent_extract(); } }