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..b52f31d1 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -528,6 +528,14 @@ template struct convertible_to_iterator } }; +template struct convertible_to_const_iterator +{ + operator typename UnorderedMap::const_iterator() + { + return typename UnorderedMap::const_iterator(); + } +}; + typedef boost::unordered_map transparent_unordered_map; @@ -544,6 +552,15 @@ transparent_unordered_map::iterator erase_overload_compile_test() return map.erase(c); } +transparent_unordered_map::const_iterator erase_const_overload_compile_test() +{ + convertible_to_const_iterator c; + transparent_unordered_map map; + transparent_unordered_map::const_iterator pos = map.begin(); + pos = c; + return map.erase(c); +} + template void test_transparent_erase() { count_reset(); @@ -620,6 +637,107 @@ 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_const_overload_compile_test() +{ + convertible_to_const_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_EQ(key::count_, 0); + + map[key(0)] = 1337; + map[key(1)] = 1338; + map[key(2)] = 1339; + BOOST_TEST_EQ(map.size(), 3); + + int const expected_key_count = static_cast(2 * map.size()); + BOOST_TEST_EQ(key::count_, expected_key_count); + + nh = map.extract(1); + BOOST_TEST_EQ(map.size(), 2); + BOOST_TEST_EQ(nh.key().x_, 1); + BOOST_TEST_EQ(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_EQ(pos->second, 1340); + + nh = map.extract(1337); + BOOST_TEST(nh.empty()); + + BOOST_TEST_EQ(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_EQ(key::count_, 1); + + map[key(0)] = 1337; + map[key(1)] = 1338; + map[key(2)] = 1339; + BOOST_TEST_EQ(map.size(), 3); + + int key_count = 1 + static_cast(2 * map.size()); + BOOST_TEST_EQ(key::count_, key_count); + + nh = map.extract(1); + ++key_count; + BOOST_TEST_EQ(map.size(), 2); + BOOST_TEST_EQ(nh.key().x_, 1); + BOOST_TEST_EQ(nh.mapped(), 1338); + BOOST_TEST_EQ(key::count_, key_count); + + nh.mapped() = 1340; + + map.insert(boost::move(nh)); + + BOOST_TEST_EQ(map.size(), 3); + + const_iterator pos = map.find(1); + ++key_count; + BOOST_TEST(pos != map.end()); + BOOST_TEST_EQ(pos->second, 1340); + BOOST_TEST_EQ(key::count_, key_count); + + nh = map.extract(1337); + ++key_count; + BOOST_TEST(nh.empty()); + BOOST_TEST_EQ(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 +760,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 +773,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 +786,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(); } }