From c30e93544d287b2ce1a13b05f78f30934885746a Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 7 Nov 2022 12:31:26 +0100 Subject: [PATCH 01/44] implemented copy_elements_from with fast branch, introduced for_all_elements_while --- include/boost/unordered/detail/foa.hpp | 112 ++++++++++++++++++++----- 1 file changed, 93 insertions(+), 19 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 29af58bf..57ce2196 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1157,9 +1157,7 @@ public: table(const table& x,const Allocator& al_): table{std::size_t(std::ceil(float(x.size())/mlf)),x.h(),x.pred(),al_} { - x.for_all_elements([this](value_type* p){ - unchecked_insert(*p); - }); + copy_elements_from(x); } table(table&& x,const Allocator& al_): @@ -1206,10 +1204,8 @@ public: copy_assign_if(al(),x.al()); }); /* noshrink: favor memory reuse over tightness */ - noshrink_reserve(x.size()); - x.for_all_elements([this](value_type* p){ - unchecked_insert(*p); - }); + noshrink_reserve(x.size()); + copy_elements_from(x); } return *this; } @@ -1502,6 +1498,66 @@ private: value_type *p; }; + void copy_elements_from(const table& x) + { + BOOST_ASSERT(empty()); + BOOST_ASSERT(this!=std::addressof(x)); + if(arrays.groups_size_mask==x.arrays.groups_size_mask){ + fast_copy_elements_from(x); + } + else{ + x.for_all_elements([this](const value_type* p){ + unchecked_insert(*p); + }); + } + } + + void fast_copy_elements_from(const table& x) + { + if(arrays.elements){ + copy_elements_array_from(x); + std::memcpy( + arrays.groups,x.arrays.groups, + (arrays.groups_size_mask+1)*sizeof(group_type)); + size_=x.size(); + } + } + + void copy_elements_array_from(const table& x) + { + copy_elements_array_from( + x, + std::integral_constant< + bool,std::is_trivially_copy_constructible::value>{}); + } + + void copy_elements_array_from(const table& x,std::true_type /* -> memcpy */) + { + std::memcpy( + arrays.elements,x.arrays.elements,x.capacity()*sizeof(value_type)); + } + + void copy_elements_array_from(const table& x,std::false_type /* -> manual */) + { + std::size_t num_constructed=0; + BOOST_TRY{ + x.for_all_elements([&,this](const value_type* p){ + construct_element(arrays.elements+(p-x.arrays.elements),*p); + ++num_constructed; + }); + } + BOOST_CATCH(...){ + if(num_constructed){ + x.for_all_elements_while([&,this](const value_type* p){ + destroy_element(arrays.elements+(p-x.arrays.elements)); + return --num_constructed!=0; + }); + } + BOOST_RETHROW + } + BOOST_CATCH_END + } + void recover_slot(unsigned char* pc) { /* If this slot potentially caused overflow, we decrease the maximum load so @@ -1704,17 +1760,13 @@ private: } BOOST_CATCH(...){ if(num_destroyed){ - for(auto pg=arrays.groups;;++pg){ - auto mask=pg->match_occupied(); - while(mask){ - auto nz=unchecked_countr_zero(mask); - recover_slot(pg,nz); - if(!(--num_destroyed))goto continue_; - mask&=mask-1; + for_all_elements_while( + [&,this](group_type* pg,unsigned int n,value_type*){ + recover_slot(pg,n); + return --num_destroyed!=0; } - } + ); } - continue_: for_all_elements(new_arrays_,[this](value_type* p){ destroy_element(p); }); @@ -1844,13 +1896,35 @@ private: static auto for_all_elements(const arrays_type& arrays_,F f) ->decltype(f(nullptr),void()) { - for_all_elements( - arrays_,[&](group_type*,unsigned int,value_type* p){return f(p);}); + for_all_elements_while(arrays_,[&](value_type* p){f(p);return true;}); } template static auto for_all_elements(const arrays_type& arrays_,F f) ->decltype(f(nullptr,0,nullptr),void()) + { + for_all_elements_while( + arrays_,[&](group_type* pg,unsigned int n,value_type* p) + {f(pg,n,p);return true;}); + } + + template + void for_all_elements_while(F f)const + { + for_all_elements_while(arrays,f); + } + + template + static auto for_all_elements_while(const arrays_type& arrays_,F f) + ->decltype(f(nullptr),void()) + { + for_all_elements_while( + arrays_,[&](group_type*,unsigned int,value_type* p){return f(p);}); + } + + template + static auto for_all_elements_while(const arrays_type& arrays_,F f) + ->decltype(f(nullptr,0,nullptr),void()) { auto p=arrays_.elements; if(!p){return;} @@ -1859,7 +1933,7 @@ private: auto mask=pg->match_really_occupied(); while(mask){ auto n=unchecked_countr_zero(mask); - f(pg,n,p+n); + if(!f(pg,n,p+n))return; mask&=mask-1; } } From 5a4071d4f0721d4e9923e1d04b59e740cbdeb82b Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 7 Nov 2022 13:47:43 +0100 Subject: [PATCH 02/44] silenced bogus CGG warning --- include/boost/unordered/detail/foa.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 57ce2196..cbf81a84 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1533,8 +1533,13 @@ private: void copy_elements_array_from(const table& x,std::true_type /* -> memcpy */) { + /* reinterpret_cast: GCC may complain about value_type not being trivially + * copy-assignable when we're relying on trivial copy constructibility. + */ std::memcpy( - arrays.elements,x.arrays.elements,x.capacity()*sizeof(value_type)); + reinterpret_cast(arrays.elements), + reinterpret_cast(x.arrays.elements), + x.capacity()*sizeof(value_type)); } void copy_elements_array_from(const table& x,std::false_type /* -> manual */) From 1d99854979841bb7a74843e7ec00c2c238e690d0 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 7 Nov 2022 17:41:47 +0100 Subject: [PATCH 03/44] worked around missing std::is_trivially_copy_constructible in GCC<=4.9 --- include/boost/unordered/detail/foa.hpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index cbf81a84..2376c8c6 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1528,7 +1529,15 @@ private: copy_elements_array_from( x, std::integral_constant< - bool,std::is_trivially_copy_constructible::value>{}); + bool, +#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000) + /* std::is_trivially_copy_constructible not provided */ + boost::has_trivial_copy::value +#else + std::is_trivially_copy_constructible::value +#endif + >{} + ); } void copy_elements_array_from(const table& x,std::true_type /* -> memcpy */) From 5eda445db0f8ed53f29cedbf4945e2ac555bc940 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 11 Dec 2022 18:49:21 +0100 Subject: [PATCH 04/44] optimized try_emplace and extended it for future use in boost::unordered_flat_set heterogeneous insert --- include/boost/unordered/detail/foa.hpp | 48 +++++++++++++++++++++----- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 734493c2..0ef15ef1 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1023,6 +1023,8 @@ inline void prefetch(const void* p) #endif } +struct try_emplace_args_t{}; + #if defined(BOOST_GCC) /* GCC's -Wshadow triggers at scenarios like this: * @@ -1316,12 +1318,10 @@ public: template BOOST_FORCEINLINE std::pair try_emplace( - Key&& k,Args&&... args) + Key&& x,Args&&... args) { return emplace_impl( - std::piecewise_construct, - std::forward_as_tuple(std::forward(k)), - std::forward_as_tuple(std::forward(args)...)); + try_emplace_args_t{},std::forward(x),std::forward(args)...); } BOOST_FORCEINLINE std::pair @@ -1510,6 +1510,37 @@ private: alloc_traits::construct(al(),p,std::forward(args)...); } + template + void construct_element(value_type* p,try_emplace_args_t,Args&&... args) + { + construct_element_from_try_emplace_args( + p, + std::integral_constant::value>{}, + std::forward(args)...); + } + + template + void construct_element_from_try_emplace_args( + value_type* p,std::false_type,Key&& x,Args&&... args) + { + alloc_traits::construct( + al(),p, + std::piecewise_construct, + std::forward_as_tuple(std::forward(x)), + std::forward_as_tuple(std::forward(args)...)); + } + + /* This overload allows boost::unordered_flat_set to internally use + * try_emplace to implement heterogeneous insert (P2363). + */ + + template + void construct_element_from_try_emplace_args( + value_type* p,std::true_type,Key&& x) + { + alloc_traits::construct(al(),p,std::forward(x)); + } + void destroy_element(value_type* p)noexcept { alloc_traits::destroy(al(),p); @@ -1631,12 +1662,11 @@ private: return type_policy::extract(x); } - template - static inline auto key_from( - std::piecewise_construct_t,const Arg1& k,const Arg2&) - ->decltype(std::get<0>(k)) + template + static inline const Key& key_from( + try_emplace_args_t,const Key& x,const Args&...) { - return std::get<0>(k); + return x; } template From 2c1c99407eaf1b7b5a714de60b4347f9460863c5 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Mon, 12 Dec 2022 12:17:40 -0800 Subject: [PATCH 05/44] Update copy_tests to test a trivially copyable value_type with an Allocator with defined construct() --- test/unordered/copy_tests.cpp | 261 +++++++++++++++++++++++++++++++++- 1 file changed, 257 insertions(+), 4 deletions(-) diff --git a/test/unordered/copy_tests.cpp b/test/unordered/copy_tests.cpp index 05c0f97a..91cf3ff2 100644 --- a/test/unordered/copy_tests.cpp +++ b/test/unordered/copy_tests.cpp @@ -225,11 +225,227 @@ namespace copy_tests { } } + template + void copy_construct_tests_std_allocator1( + T*, test::random_generator const& generator) + { + typename T::hasher hf; + typename T::key_equal eq; + typename T::allocator_type al; + + { + test::check_instances check_; + + T x; + T y(x); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + BOOST_TEST(test::detail::tracker.count_allocations == 0); + test::check_equivalent_keys(y); + } + + { + test::check_instances check_; + + T x(0); + T y(x); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + BOOST_TEST(test::detail::tracker.count_allocations == 0); + test::check_equivalent_keys(y); + } + + { + test::check_instances check_; + + test::random_values v(1000, generator); + + T x(v.begin(), v.end()); + T y(x); + test::unordered_equivalence_tester equivalent(x); + BOOST_TEST(equivalent(y)); + test::check_equivalent_keys(y); + } + + { + test::check_instances check_; + + // In this test I drop the original containers max load factor, so it + // is much lower than the load factor. The hash table is not allowed + // to rehash, but the destination container should probably allocate + // enough buckets to decrease the load factor appropriately. + test::random_values v(1000, generator); + T x(v.begin(), v.end()); + x.max_load_factor(x.load_factor() / 4); + T y(x); + test::unordered_equivalence_tester equivalent(x); + BOOST_TEST(equivalent(y)); + // This isn't guaranteed: + BOOST_TEST(y.load_factor() < y.max_load_factor()); + test::check_equivalent_keys(y); + } + } + + template + void copy_construct_tests_std_allocator2( + T*, test::random_generator const& generator) + { + typename T::hasher hf(1); + typename T::key_equal eq(1); + typename T::allocator_type al; + + { + test::check_instances check_; + + T x(0, hf, eq, al); + T y(x); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + BOOST_TEST(test::detail::tracker.count_allocations == 0); + test::check_equivalent_keys(y); + } + + { + test::check_instances check_; + + T x(10000, hf, eq, al); + T y(x); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + test::check_equivalent_keys(y); + } + + { + test::check_instances check_; + + T x(0, hf, eq, al); + T y(x, al); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + BOOST_TEST(test::selected_count(y.get_allocator()) == 0); + BOOST_TEST(test::detail::tracker.count_allocations == 0); + test::check_equivalent_keys(y); + } + + { + test::check_instances check_; + + T x(1000, hf, eq, al); + T y(x, al); + BOOST_TEST(y.empty()); + BOOST_TEST(test::equivalent(y.hash_function(), hf)); + BOOST_TEST(test::equivalent(y.key_eq(), eq)); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(x.max_load_factor() == y.max_load_factor()); + BOOST_TEST(test::selected_count(y.get_allocator()) == 0); + test::check_equivalent_keys(y); + } + + { + test::check_instances check_; + + test::random_values v; + + T x(v.begin(), v.end(), 0, hf, eq, al); + T y(x); + test::unordered_equivalence_tester equivalent(x); + BOOST_TEST(equivalent(y)); + test::check_equivalent_keys(y); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(test::detail::tracker.count_allocations == 0); + } + + { + test::check_instances check_; + + test::random_values v(1000, generator); + + T x(v.begin(), v.end(), 0, hf, eq, al); + T y(x); + test::unordered_equivalence_tester equivalent(x); + BOOST_TEST(equivalent(y)); + test::check_equivalent_keys(y); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + } + + { + test::check_instances check_; + + test::random_values v; + + T x(v.begin(), v.end(), 0, hf, eq, al); + T y(x, al); + test::unordered_equivalence_tester equivalent(x); + BOOST_TEST(equivalent(y)); + test::check_equivalent_keys(y); + BOOST_TEST(test::selected_count(y.get_allocator()) == 0); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + BOOST_TEST(test::detail::tracker.count_allocations == 0); + } + + { + test::check_instances check_; + + test::random_values v(500, generator); + + T x(v.begin(), v.end(), 0, hf, eq, al); + T y(x, al); + test::unordered_equivalence_tester equivalent(x); + BOOST_TEST(equivalent(y)); + test::check_equivalent_keys(y); + BOOST_TEST(test::selected_count(y.get_allocator()) == 0); + BOOST_TEST(test::equivalent(y.get_allocator(), al)); + } + } + using test::default_generator; using test::generate_collisions; using test::limited_range; #ifdef BOOST_UNORDERED_FOA_TESTS + template struct allocator + { + using value_type = T; + + allocator() = default; + allocator(allocator const&) = default; + allocator(allocator&&) = default; + + template allocator(allocator const&) {} + + T* allocate(std::size_t n) + { + return static_cast(::operator new(sizeof(value_type) * n)); + } + + void deallocate(T* p, std::size_t) { ::operator delete(p); } + + friend inline bool operator==(allocator const&, allocator const&) + { + return true; + } + + friend inline bool operator!=(allocator const&, allocator const&) + { + return false; + } + }; + boost::unordered_flat_set >* test_set; boost::unordered_flat_map >* test_map_no_select_copy; + boost::unordered_flat_set >* test_set_trivially_copyable; + boost::unordered_flat_map > >* test_map_trivially_copyable; + + boost::unordered_flat_set >* test_set_trivially_copyable_std_allocator; + boost::unordered_flat_map > >* + test_map_trivially_copyable_std_allocator; + + boost::unordered_flat_set >* + test_set_trivially_copyable_no_construct; + boost::unordered_flat_map > >* + test_map_trivially_copyable_no_construct; + + // clang-format off UNORDERED_TEST(copy_construct_tests1, - ((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)(test_set_no_select_copy)(test_map_no_select_copy))( - (default_generator)(generate_collisions)(limited_range))) + ((test_set)(test_map)(test_set_select_copy)(test_map_select_copy) + (test_set_no_select_copy)(test_map_no_select_copy) + (test_set_trivially_copyable)(test_map_trivially_copyable)) + ((default_generator)(generate_collisions)(limited_range))) UNORDERED_TEST(copy_construct_tests2, - ((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)(test_set_no_select_copy)(test_map_no_select_copy))( - (default_generator)(generate_collisions)(limited_range))) + ((test_set)(test_map)(test_set_select_copy)(test_map_select_copy) + (test_set_no_select_copy)(test_map_no_select_copy) + (test_set_trivially_copyable)(test_map_trivially_copyable)) + ((default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(copy_construct_tests_std_allocator1, + ((test_set_trivially_copyable_std_allocator) + (test_map_trivially_copyable_std_allocator) + (test_set_trivially_copyable_no_construct) + (test_map_trivially_copyable_no_construct)) + ((default_generator)(generate_collisions)(limited_range))) + + UNORDERED_TEST(copy_construct_tests_std_allocator2, + ((test_set_trivially_copyable_std_allocator) + (test_map_trivially_copyable_std_allocator) + (test_set_trivially_copyable_no_construct) + (test_map_trivially_copyable_no_construct)) + ((default_generator)(generate_collisions)(limited_range))) + // clang-format on #else boost::unordered_set >* test_set; From 47e205487dfdebbb49fa09abf7c8ce7a13c396fc Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Mon, 12 Dec 2022 12:16:49 -0800 Subject: [PATCH 06/44] Fix potential integer overflow in test::hash --- test/objects/test.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 0bcf78e8..b1a253e5 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -230,18 +230,18 @@ namespace test { std::size_t operator()(int x) const { - int result; + unsigned result; switch (type_) { case 1: - result = x; + result = static_cast(x); break; case 2: - result = x * 7; + result = static_cast(x) * 7; break; default: - result = x * 256; + result = static_cast(x) * 256; } - return static_cast(result); + return result; } friend bool operator==(hash const& x1, hash const& x2) From 91eddbabe8978baf5b46c951f6cffa343d9aec55 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 12 Dec 2022 11:26:12 +0100 Subject: [PATCH 07/44] restricted memcpy to allocators known to not have fancy construct() --- include/boost/unordered/detail/foa.hpp | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 0ef15ef1..ccf48227 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -1025,6 +1026,47 @@ inline void prefetch(const void* p) struct try_emplace_args_t{}; +template +struct is_std_allocator:std::false_type{}; + +template +struct is_std_allocator>:std::true_type{}; + +/* std::allocator::construct marked as deprecated */ +#if defined(_LIBCPP_SUPPRESS_DEPRECATED_PUSH) +_LIBCPP_SUPPRESS_DEPRECATED_PUSH +#elif defined(_STL_DISABLE_DEPRECATED_WARNING) +_STL_DISABLE_DEPRECATED_WARNING +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) +#endif + +template +struct alloc_has_construct +{ +private: + template + static decltype( + std::declval().construct( + std::declval(),std::declval()...), + std::true_type{} + ) check(int); + + template static std::false_type check(...); + +public: + static constexpr bool value=decltype(check(0))::value; +}; + +#if defined(_LIBCPP_SUPPRESS_DEPRECATED_POP) +_LIBCPP_SUPPRESS_DEPRECATED_POP +#elif defined(_STL_RESTORE_DEPRECATED_WARNING) +_STL_RESTORE_DEPRECATED_WARNING +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + #if defined(BOOST_GCC) /* GCC's -Wshadow triggers at scenarios like this: * @@ -1590,6 +1632,9 @@ private: #else std::is_trivially_copy_constructible::value #endif + &&( + is_std_allocator::value|| + !alloc_has_construct::value) >{} ); } From 7befee3bd6920adc75fff9fff76731f6e2b6a611 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 1 Dec 2022 10:51:04 -0800 Subject: [PATCH 08/44] Update swap_exception_tests to exclude FOA containers and add test for throwing asserts when comparing allocators --- test/exception/swap_exception_tests.cpp | 166 ++++++++++++++++++++++-- 1 file changed, 154 insertions(+), 12 deletions(-) diff --git a/test/exception/swap_exception_tests.cpp b/test/exception/swap_exception_tests.cpp index 545d607a..e60ab939 100644 --- a/test/exception/swap_exception_tests.cpp +++ b/test/exception/swap_exception_tests.cpp @@ -4,15 +4,40 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include "./containers.hpp" +#define BOOST_ENABLE_ASSERT_HANDLER +#include -#if defined(BOOST_UNORDERED_FOA_TESTS) -#define BOOST_UNORDERED_FOA_WEAK_GUARANTEE_SWAP_EXCEPTIONS_TESTS -#endif +#include "./containers.hpp" #include "../helpers/invariants.hpp" #include "../helpers/random_values.hpp" #include "../helpers/tracker.hpp" +#include "../objects/test.hpp" + +#include + +namespace boost { + void assertion_failed( + char const* expr, char const* function, char const* file, long line) + { + std::stringstream ss; + ss << expr << "\nin " << function << " failed at : " << file << ", line " + << line; + + throw std::runtime_error(ss.str()); + } + + void assertion_failed_msg(char const* expr, char const* msg, + char const* function, char const* file, long line) + { + std::stringstream ss; + ss << expr << "\nin " << function << " failed at : " << file << ", line " + << line << "\n" + << msg; + + throw std::runtime_error(ss.str()); + } +} // namespace boost #if defined(BOOST_MSVC) #pragma warning(disable : 4512) // assignment operator could not be generated @@ -39,15 +64,10 @@ template struct self_swap_base : public test::exception_base void check BOOST_PREVENT_MACRO_SUBSTITUTION(T const& x) const { - std::string scope(test::scope); + (void)x; - // TODO: In C++11 exceptions are only allowed in the swap function. - BOOST_TEST(scope == "hash::hash(hash)" || - scope == "hash::operator=(hash)" || - scope == "equal_to::equal_to(equal_to)" || - scope == "equal_to::operator=(equal_to)"); - - test::check_equivalent_keys(x); + BOOST_ERROR("An exception leaked when it should not have. Allocator " + "equality assertion must precede all other ops"); } }; @@ -140,11 +160,133 @@ template struct swap_test4 : swap_base swap_test4() : swap_base(10, 10, 1, 2) {} }; +template struct unequal_alloc_swap_base : public test::exception_base +{ + const test::random_values x_values, y_values; + const T initial_x, initial_y; + + typedef typename T::hasher hasher; + typedef typename T::key_equal key_equal; + typedef typename T::allocator_type allocator_type; + + unequal_alloc_swap_base(unsigned int count1, unsigned int count2) + : x_values(count1, test::limited_range), + y_values(count2, test::limited_range), + initial_x(x_values.begin(), x_values.end(), 0, allocator_type(1337)), + initial_y(y_values.begin(), y_values.end(), 0, allocator_type(7331)) + { + } + + struct data_type + { + data_type(T const& x_, T const& y_) : x(x_), y(y_) {} + + T x, y; + }; + + data_type init() const { return data_type(initial_x, initial_y); } + + void run(data_type& d) const + { + bool assert_threw = false; + + BOOST_TEST(d.x.get_allocator() != d.y.get_allocator()); + + try { + d.x.swap(d.y); + } catch (std::runtime_error&) { + assert_threw = true; + } + + DISABLE_EXCEPTIONS; + BOOST_TEST(assert_threw); + test::check_container(d.x, this->x_values); + test::check_equivalent_keys(d.x); + test::check_container(d.y, this->y_values); + test::check_equivalent_keys(d.y); + } + + void check BOOST_PREVENT_MACRO_SUBSTITUTION(data_type const& d) const + { + std::string scope(test::scope); + + // TODO: In C++11 exceptions are only allowed in the swap function. + BOOST_TEST(scope == "hash::hash(hash)" || + scope == "hash::operator=(hash)" || + scope == "equal_to::equal_to(equal_to)" || + scope == "equal_to::operator=(equal_to)"); + + test::check_equivalent_keys(d.x); + test::check_equivalent_keys(d.y); + } +}; + +template struct unequal_alloc_swap_test1 : unequal_alloc_swap_base +{ + unequal_alloc_swap_test1() : unequal_alloc_swap_base(0, 0) {} +}; + +template struct unequal_alloc_swap_test2 : unequal_alloc_swap_base +{ + unequal_alloc_swap_test2() : unequal_alloc_swap_base(0, 10) {} +}; + +template struct unequal_alloc_swap_test3 : unequal_alloc_swap_base +{ + unequal_alloc_swap_test3() : unequal_alloc_swap_base(10, 0) {} +}; + +template struct unequal_alloc_swap_test4 : unequal_alloc_swap_base +{ + unequal_alloc_swap_test4() : unequal_alloc_swap_base(10, 10) {} +}; + +#if defined(BOOST_UNORDERED_FOA_TESTS) + +using unordered_flat_set = boost::unordered_flat_set, + std::equal_to, test::allocator1 >; +using unordered_flat_map = boost::unordered_flat_map, + std::equal_to, test::allocator1 > >; + +#define SWAP_CONTAINER_SEQ (unordered_flat_set)(unordered_flat_map) + +#else + +typedef boost::unordered_set, std::equal_to, + test::allocator1 > + unordered_set; +typedef boost::unordered_map, std::equal_to, + test::allocator1 > > + unordered_map; +typedef boost::unordered_multiset, std::equal_to, + test::allocator1 > + unordered_multiset; +typedef boost::unordered_multimap, + std::equal_to, test::allocator1 > > + unordered_multimap; + +#define SWAP_CONTAINER_SEQ \ + (unordered_set)(unordered_map)(unordered_multiset)(unordered_multimap) +#endif + +// FOA containers deliberately choose to not offer the strong exception +// guarantee so we can't reliably test what happens if swapping one of the data +// members throws +// // clang-format off +#if !defined(BOOST_UNORDERED_FOA_TESTS) EXCEPTION_TESTS( (self_swap_test1)(self_swap_test2) (swap_test1)(swap_test2)(swap_test3)(swap_test4), CONTAINER_SEQ) +#endif + +// want to prove that when assertions are defined as throwing operations that we +// uphold invariants +EXCEPTION_TESTS( + (unequal_alloc_swap_test1)(unequal_alloc_swap_test2) + (unequal_alloc_swap_test3)(unequal_alloc_swap_test4), + SWAP_CONTAINER_SEQ) // clang-format on RUN_TESTS() From 534170a942ff3acf90cbf1be24ee122b84f24052 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 1 Dec 2022 10:51:30 -0800 Subject: [PATCH 09/44] Remove foa-related macro used for relaxing invariant checking in check_equivalent_keys --- test/helpers/invariants.hpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index 7fbe25a5..a677a91f 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -54,20 +54,10 @@ namespace test { if (test::has_unique_keys::value && count != 1) BOOST_ERROR("Non-unique key."); -#if !defined(BOOST_UNORDERED_FOA_WEAK_GUARANTEE_SWAP_EXCEPTIONS_TESTS) - // we conditionally compile this check because our FOA implementation only - // exhibits the weak guarantee when swapping throws - // - // in this case, the hasher may be changed before the predicate and the - // arrays are swapped in which case, we can can find an element by - // iteration but unfortunately, it's in the wrong slot according to the - // new hash function so count(key) can wind up returning nothing when - // there really is something if (x1.count(key) != count) { BOOST_ERROR("Incorrect output of count."); std::cerr << x1.count(key) << "," << count << "\n"; } -#endif #ifndef BOOST_UNORDERED_FOA_TESTS // Check that the keys are in the correct bucket and are From b1d43d3ca575a40fb38debe18259642723956d2f Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 1 Dec 2022 10:52:02 -0800 Subject: [PATCH 10/44] Update FOA containers to require nothrow swappability of Hash, KeyEqual members and ensure that throwing assertions uphold strong guarantee --- include/boost/unordered/detail/foa.hpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index ccf48227..e70c6ce5 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -69,6 +69,12 @@ }while(0) #endif +#define BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred) \ + static_assert(boost::is_nothrow_swappable::value, \ + "Template parameter Hash is required to be nothrow Swappable."); \ + static_assert(boost::is_nothrow_swappable::value, \ + "Template parameter Pred is required to be nothrow Swappable"); + namespace boost{ namespace unordered{ namespace detail{ @@ -1412,16 +1418,15 @@ public: void swap(table& x) noexcept( - alloc_traits::is_always_equal::value&& - boost::is_nothrow_swappable::value&& - boost::is_nothrow_swappable::value) + alloc_traits::propagate_on_container_swap::value|| + alloc_traits::is_always_equal::value) { + BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred) + static constexpr auto pocs= alloc_traits::propagate_on_container_swap::value; using std::swap; - swap(h(),x.h()); - swap(pred(),x.pred()); if_constexpr([&,this]{ swap_if(al(),x.al()); }, @@ -1429,6 +1434,9 @@ public: BOOST_ASSERT(al()==x.al()); (void)this; /* makes sure captured this is used */ }); + + swap(h(),x.h()); + swap(pred(),x.pred()); swap(size_,x.size_); swap(arrays,x.arrays); swap(ml,x.ml); @@ -2075,6 +2083,7 @@ private: #undef BOOST_UNORDERED_ASSUME #undef BOOST_UNORDERED_HAS_BUILTIN +#undef BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED #ifdef BOOST_UNORDERED_LITTLE_ENDIAN_NEON #undef BOOST_UNORDERED_LITTLE_ENDIAN_NEON #endif From 75ea43823eb3f17ac9a233ccca8d5f67b42a5a1f Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 1 Dec 2022 12:02:15 -0800 Subject: [PATCH 11/44] Update test Hash, KeyEqual to be nothrow swappable --- test/objects/exception.hpp | 25 +++++++++++++++++++++++++ test/objects/minimal.hpp | 10 ++++++++++ 2 files changed, 35 insertions(+) diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp index b5570e1d..68595eab 100644 --- a/test/objects/exception.hpp +++ b/test/objects/exception.hpp @@ -227,8 +227,21 @@ namespace test { } return x1.tag_ != x2.tag_; } + +#if defined(BOOST_UNORDERED_FOA_TESTS) + friend void swap(hash&, hash&) noexcept; +#endif }; +#if defined(BOOST_UNORDERED_FOA_TESTS) + void swap(hash& lhs, hash& rhs) noexcept + { + int tag = lhs.tag_; + lhs.tag_ = rhs.tag_; + rhs.tag_ = tag; + } +#endif + class less { int tag_; @@ -364,8 +377,20 @@ namespace test { } friend less create_compare(equal_to x) { return less(x.tag_); } +#if defined(BOOST_UNORDERED_FOA_TESTS) + friend void swap(equal_to&, equal_to&) noexcept; +#endif }; +#if defined(BOOST_UNORDERED_FOA_TESTS) + void swap(equal_to& lhs, equal_to& rhs) noexcept + { + int tag = lhs.tag_; + lhs.tag_ = rhs.tag_; + rhs.tag_ = tag; + } +#endif + template class allocator { public: diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp index cb1fe9b0..c2690db3 100644 --- a/test/objects/minimal.hpp +++ b/test/objects/minimal.hpp @@ -206,6 +206,11 @@ namespace test { hash& operator=(hash const&) { return *this; } ~hash() {} +#if defined(BOOST_UNORDERED_FOA_TESTS) + hash(hash&&) = default; + hash& operator=(hash&&) = default; +#endif + std::size_t operator()(T const&) const { return 0; } #if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED ampersand_operator_used operator&() const @@ -224,6 +229,11 @@ namespace test { equal_to& operator=(equal_to const&) { return *this; } ~equal_to() {} +#if defined(BOOST_UNORDERED_FOA_TESTS) + equal_to(equal_to&&) = default; + equal_to& operator=(equal_to&&) = default; +#endif + bool operator()(T const&, T const&) const { return true; } #if BOOST_UNORDERED_CHECK_ADDR_OPERATOR_NOT_USED ampersand_operator_used operator&() const From 4ac3dcc90c360fdb56c48d2d1582787996ed2cd4 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 1 Dec 2022 14:31:19 -0800 Subject: [PATCH 12/44] Update assign_exception_tests to assert strong guarantee around Hash, KeyEqual pairing --- test/exception/assign_exception_tests.cpp | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/test/exception/assign_exception_tests.cpp b/test/exception/assign_exception_tests.cpp index c036da52..e96e8465 100644 --- a/test/exception/assign_exception_tests.cpp +++ b/test/exception/assign_exception_tests.cpp @@ -60,6 +60,9 @@ template struct assign_base : public test::exception_base test::random_values x_values, y_values; T x, y; + int t1; + int t2; + typedef typename T::hasher hasher; typedef typename T::key_equal key_equal; typedef typename T::allocator_type allocator_type; @@ -67,7 +70,10 @@ template struct assign_base : public test::exception_base assign_base(int tag1, int tag2, float mlf1 = 1.0, float mlf2 = 1.0) : x_values(), y_values(), x(0, hasher(tag1), key_equal(tag1), allocator_type(tag1)), - y(0, hasher(tag2), key_equal(tag2), allocator_type(tag2)) + y(0, hasher(tag2), key_equal(tag2), allocator_type(tag2)), + t1(tag1), + t2(tag2) + { x.max_load_factor(mlf1); y.max_load_factor(mlf2); @@ -89,6 +95,22 @@ template struct assign_base : public test::exception_base { test::check_equivalent_keys(x1); + if (x1.hash_function() == hasher(t1)) { + BOOST_TEST(x1.key_eq() == key_equal(t1)); + } + + if (x1.hash_function() == hasher(t2)) { + BOOST_TEST(x1.key_eq() == key_equal(t2)); + } + + if (x1.key_eq() == key_equal(t1)) { + BOOST_TEST(x1.hash_function() == hasher(t1)); + } + + if (x1.key_eq() == key_equal(t2)) { + BOOST_TEST(x1.hash_function() == hasher(t2)); + } + // If the container is empty at the point of the exception, the // internal structure is hidden, this exposes it, at the cost of // messing up the data. From 260b573d8dddda9e200f062b28a1e81a3da80a06 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 1 Dec 2022 14:32:07 -0800 Subject: [PATCH 13/44] Update FOA implementation to exhibit strong guarantee for Hash, KeyEqual in copy assignment --- include/boost/unordered/detail/foa.hpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index e70c6ce5..9b3fec58 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1266,13 +1266,28 @@ public: table& operator=(const table& x) { + BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred) + static constexpr auto pocca= alloc_traits::propagate_on_container_copy_assignment::value; if(this!=std::addressof(x)){ - clear(); - h()=x.h(); - pred()=x.pred(); + // if copy construction here winds up throwing, the container is still + // left intact so we perform these operations first + hasher tmp_h=x.h(); + key_equal tmp_p=x.pred(); + + // already noexcept, clear() before we swap the Hash, Pred just in case + // the clear() impl relies on them at some point in the future + clear(); + + // because we've asserted at compile-time that Hash and Pred are nothrow + // swappable, we can safely mutate our source container and maintain + // consistency between the Hash, Pred compatibility + using std::swap; + swap(h(),tmp_h); + swap(pred(),tmp_p); + if_constexpr([&,this]{ if(al()!=x.al())reserve(0); copy_assign_if(al(),x.al()); From 2043f98593353310a90660997cbeedfd4549993f Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 1 Dec 2022 15:50:28 -0800 Subject: [PATCH 14/44] Add strong exception guarantees around Hash, KeyEqual for move_assign_exception_tests --- .../exception/move_assign_exception_tests.cpp | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/test/exception/move_assign_exception_tests.cpp b/test/exception/move_assign_exception_tests.cpp index 6beb6820..14feed03 100644 --- a/test/exception/move_assign_exception_tests.cpp +++ b/test/exception/move_assign_exception_tests.cpp @@ -20,6 +20,7 @@ template struct move_assign_base : public test::exception_base { test::random_values x_values, y_values; T x, y; + int t1, t2; typedef typename T::hasher hasher; typedef typename T::key_equal key_equal; @@ -28,7 +29,9 @@ template struct move_assign_base : public test::exception_base move_assign_base(int tag1, int tag2, float mlf1 = 1.0, float mlf2 = 1.0) : x_values(), y_values(), x(0, hasher(tag1), key_equal(tag1), allocator_type(tag1)), - y(0, hasher(tag2), key_equal(tag2), allocator_type(tag2)) + y(0, hasher(tag2), key_equal(tag2), allocator_type(tag2)), + t1(tag1), + t2(tag2) { x.max_load_factor(mlf1); y.max_load_factor(mlf2); @@ -52,6 +55,22 @@ template struct move_assign_base : public test::exception_base { test::check_equivalent_keys(x1); + if (x1.hash_function() == hasher(t1)) { + BOOST_TEST(x1.key_eq() == key_equal(t1)); + } + + if (x1.hash_function() == hasher(t2)) { + BOOST_TEST(x1.key_eq() == key_equal(t2)); + } + + if (x1.key_eq() == key_equal(t1)) { + BOOST_TEST(x1.hash_function() == hasher(t1)); + } + + if (x1.key_eq() == key_equal(t2)) { + BOOST_TEST(x1.hash_function() == hasher(t2)); + } + // If the container is empty at the point of the exception, the // internal structure is hidden, this exposes it, at the cost of // messing up the data. From c8910e8007c3e476a17f1c177c87f39bf77e6f35 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 1 Dec 2022 15:51:22 -0800 Subject: [PATCH 15/44] Update FOA move assignment operator to uphold the strong guarantee for Hash, KeyEqual --- include/boost/unordered/detail/foa.hpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 9b3fec58..41a1f153 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1306,19 +1306,32 @@ public: table& operator=(table&& x) noexcept( - alloc_traits::is_always_equal::value&& - std::is_nothrow_move_assignable::value&& - std::is_nothrow_move_assignable::value) + alloc_traits::propagate_on_container_move_assignment::value|| + alloc_traits::is_always_equal::value) { + BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred) + static constexpr auto pocma= alloc_traits::propagate_on_container_move_assignment::value; if(this!=std::addressof(x)){ + /* Given ambiguity in implementation strategies briefly discussed here: + * https://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2227 + * + * we opt into requiring nothrow swappability and eschew the move + * operations associated with Hash, Pred. + * + * To this end, we ensure that the user never has to consider the + * moved-from state of their Hash, Pred objects + */ + + using std::swap; + clear(); - h()=std::move(x.h()); - pred()=std::move(x.pred()); + swap(h(),x.h()); + swap(pred(),x.pred()); + if(pocma||al()==x.al()){ - using std::swap; reserve(0); move_assign_if(al(),x.al()); swap(size_,x.size_); From 0ab4e125021ae4f92a73e03db3161197e910020d Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 1 Dec 2022 15:51:35 -0800 Subject: [PATCH 16/44] Update noexcept tests for new FOA requirements --- test/unordered/noexcept_tests.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/unordered/noexcept_tests.cpp b/test/unordered/noexcept_tests.cpp index 0cb4b521..72270a1c 100644 --- a/test/unordered/noexcept_tests.cpp +++ b/test/unordered/noexcept_tests.cpp @@ -437,13 +437,11 @@ UNORDERED_AUTO_TEST (prelim_allocator_checks) { using test::default_generator; #ifdef BOOST_UNORDERED_FOA_TESTS -boost::unordered_flat_set >* - throwing_set_alloc1; +boost::unordered_flat_set >* throwing_set_alloc1; -boost::unordered_flat_set >* - throwing_set_alloc2; +boost::unordered_flat_set >* throwing_set_alloc2; UNORDERED_TEST(test_nothrow_move_assign_when_noexcept, ((throwing_set_alloc1)(throwing_set_alloc2))((default_generator))) From 0ad6ccb0b934289a0d83de3ddb8f5aba722aa4be Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Mon, 5 Dec 2022 13:08:48 -0800 Subject: [PATCH 17/44] Update FOA noexcept docs for move assignment, swap --- doc/unordered/unordered_flat_map.adoc | 22 +++++++++------------- doc/unordered/unordered_flat_set.adoc | 22 +++++++++------------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/doc/unordered/unordered_flat_map.adoc b/doc/unordered/unordered_flat_map.adoc index a7f4c7fe..27af35cd 100644 --- a/doc/unordered/unordered_flat_map.adoc +++ b/doc/unordered/unordered_flat_map.adoc @@ -98,9 +98,8 @@ namespace boost { xref:#unordered_flat_map_destructor[~unordered_flat_map](); unordered_flat_map& xref:#unordered_flat_map_copy_assignment[operator++=++](const unordered_flat_map& other); unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other) - noexcept(boost::allocator_traits::is_always_equal::value && - boost::is_nothrow_move_assignable_v && - boost::is_nothrow_move_assignable_v); + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value); unordered_flat_map& xref:#unordered_flat_map_initializer_list_assignment[operator++=++](std::initializer_list); allocator_type xref:#unordered_flat_map_get_allocator[get_allocator]() const noexcept; @@ -154,9 +153,8 @@ namespace boost { template size_type xref:#unordered_flat_map_transparent_erase_by_key[erase](K&& k); iterator xref:#unordered_flat_map_erase_range[erase](const_iterator first, const_iterator last); void xref:#unordered_flat_map_swap[swap](unordered_flat_map& other) - noexcept(boost::allocator_traits::is_always_equal::value && - boost::is_nothrow_swappable_v && - boost::is_nothrow_swappable_v); + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); void xref:#unordered_flat_map_clear[clear]() noexcept; template @@ -606,11 +604,10 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse ==== Move Assignment ```c++ unordered_flat_map& operator=(unordered_flat_map&& other) - noexcept(boost::allocator_traits::is_always_equal::value && - boost::is_nothrow_move_assignable_v && - boost::is_nothrow_move_assignable_v); + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value); ``` -The move assignment operator. Destroys previously existing elements, move-assigns the hash function and predicate from `other`, +The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`, and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`. If at this point the allocator is equal to `other.get_allocator()`, the internal bucket array of `other` is transferred directly to the new container; otherwise, inserts move-constructed copies of the elements of `other`. @@ -1043,9 +1040,8 @@ Throws:;; Nothing in this implementation (neither the `hasher` nor the `key_equa ==== swap ```c++ void swap(unordered_flat_map& other) - noexcept(boost::allocator_traits::is_always_equal::value && - boost::is_nothrow_swappable_v && - boost::is_nothrow_swappable_v); + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); ``` Swaps the contents of the container with the parameter. diff --git a/doc/unordered/unordered_flat_set.adoc b/doc/unordered/unordered_flat_set.adoc index 33fe5dfa..cd22733a 100644 --- a/doc/unordered/unordered_flat_set.adoc +++ b/doc/unordered/unordered_flat_set.adoc @@ -93,9 +93,8 @@ namespace boost { xref:#unordered_flat_set_destructor[~unordered_flat_set](); unordered_flat_set& xref:#unordered_flat_set_copy_assignment[operator++=++](const unordered_flat_set& other); unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other) - noexcept(boost::allocator_traits::is_always_equal::value && - boost::is_nothrow_move_assignable_v && - boost::is_nothrow_move_assignable_v); + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value); unordered_flat_set& xref:#unordered_flat_set_initializer_list_assignment[operator++=++](std::initializer_list); allocator_type xref:#unordered_flat_set_get_allocator[get_allocator]() const noexcept; @@ -128,9 +127,8 @@ namespace boost { template size_type xref:#unordered_flat_set_transparent_erase_by_key[erase](K&& k); iterator xref:#unordered_flat_set_erase_range[erase](const_iterator first, const_iterator last); void xref:#unordered_flat_set_swap[swap](unordered_flat_set& other) - noexcept(boost::allocator_traits::is_always_equal::value && - boost::is_nothrow_swappable_v && - boost::is_nothrow_swappable_v); + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); void xref:#unordered_flat_set_clear[clear]() noexcept; template @@ -565,11 +563,10 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse ==== Move Assignment ```c++ unordered_flat_set& operator=(unordered_flat_set&& other) - noexcept(boost::allocator_traits::is_always_equal::value && - boost::is_nothrow_move_assignable_v && - boost::is_nothrow_move_assignable_v); + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_move_assignment::value); ``` -The move assignment operator. Destroys previously existing elements, move-assigns the hash function and predicate from `other`, +The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`, and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`. If at this point the allocator is equal to `other.get_allocator()`, the internal bucket array of `other` is transferred directly to the new container; otherwise, inserts move-constructed copies of the elements of `other`. @@ -863,9 +860,8 @@ Throws:;; Nothing in this implementation (neither the `hasher` nor the `key_equa ==== swap ```c++ void swap(unordered_flat_set& other) - noexcept(boost::allocator_traits::is_always_equal::value && - boost::is_nothrow_swappable_v && - boost::is_nothrow_swappable_v); + noexcept(boost::allocator_traits::is_always_equal::value || + boost::allocator_traits::propagate_on_container_swap::value); ``` Swaps the contents of the container with the parameter. From ee1515189bd7545e0089752a515b4647c2a57230 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Mon, 14 Nov 2022 15:16:06 -0800 Subject: [PATCH 18/44] Add transparent tests for unordered_map's try_emplace() --- test/unordered/transparent_tests.cpp | 81 ++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index 9852fa84..8d74fc2a 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -1478,6 +1478,83 @@ template void test_map_non_transparent_extract() #endif } +template void test_map_transparent_try_emplace() +{ + count_reset(); + + typedef typename UnorderedMap::iterator iterator; + + UnorderedMap map; + + map.insert(std::make_pair(0, 1337)); + map.insert(std::make_pair(1, 1338)); + map.insert(std::make_pair(2, 1339)); + map.insert(std::make_pair(0, 1340)); + map.insert(std::make_pair(0, 1341)); + map.insert(std::make_pair(0, 1342)); + + int key_count = key::count_; + + std::pair r = map.try_emplace(0, 7331); + BOOST_TEST(r.first == map.find(0)); + BOOST_TEST_NOT(r.second); + BOOST_TEST_EQ(key::count_, key_count); + + r = map.try_emplace(4, 7331); + BOOST_TEST(r.first == map.find(4)); + BOOST_TEST(r.second); + BOOST_TEST_EQ(key::count_, key_count + 1); + + key_count = key::count_; + + iterator p = map.try_emplace(map.cbegin(), 0, 7331); + BOOST_TEST(p == map.find(0)); + BOOST_TEST_EQ(key::count_, key_count); + + p = map.try_emplace(map.begin(), 5, 7331); + BOOST_TEST(p == map.find(5)); + BOOST_TEST_EQ(key::count_, key_count + 1); +} + +template void test_map_non_transparent_try_emplace() +{ + count_reset(); + + typedef typename UnorderedMap::iterator iterator; + + UnorderedMap map; + + map.insert(std::make_pair(0, 1337)); + map.insert(std::make_pair(1, 1338)); + map.insert(std::make_pair(2, 1339)); + map.insert(std::make_pair(0, 1340)); + map.insert(std::make_pair(0, 1341)); + map.insert(std::make_pair(0, 1342)); + + int key_count = key::count_; + + std::pair r = map.try_emplace(0, 7331); + BOOST_TEST_EQ(key::count_, key_count + 1); + BOOST_TEST(r.first == map.find(0)); + BOOST_TEST_NOT(r.second); + + key_count = key::count_; + r = map.try_emplace(4, 7331); + BOOST_TEST_EQ(key::count_, key_count + 2); + BOOST_TEST(r.first == map.find(4)); + BOOST_TEST(r.second); + + key_count = key::count_; + iterator p = map.try_emplace(map.cbegin(), 0, 7331); + BOOST_TEST_EQ(key::count_, key_count + 1); + BOOST_TEST(p == map.find(0)); + + key_count = key::count_; + p = map.try_emplace(map.begin(), 5, 7331); + BOOST_TEST_EQ(key::count_, key_count + 2); + BOOST_TEST(p == map.find(5)); +} + #ifndef BOOST_UNORDERED_FOA_TESTS transparent_unordered_set::node_type set_extract_overload_compile_test() { @@ -1646,6 +1723,7 @@ void test_unordered_map() test_map_transparent_equal_range(); test_map_transparent_erase(); test_map_transparent_extract(); + test_map_transparent_try_emplace(); } { @@ -1658,6 +1736,7 @@ void test_unordered_map() test_map_non_transparent_equal_range(); test_map_non_transparent_erase(); test_map_non_transparent_extract(); + test_map_non_transparent_try_emplace(); } { @@ -1671,6 +1750,7 @@ void test_unordered_map() test_map_non_transparent_equal_range(); test_map_non_transparent_erase(); test_map_non_transparent_extract(); + test_map_non_transparent_try_emplace(); } { @@ -1684,6 +1764,7 @@ void test_unordered_map() test_map_non_transparent_equal_range(); test_map_non_transparent_erase(); test_map_non_transparent_extract(); + test_map_non_transparent_try_emplace(); } } From bf2b5217892f87f7ad117dcec491d38a7a9507dd Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Mon, 14 Nov 2022 15:17:32 -0800 Subject: [PATCH 19/44] Add transparent try_emplace() --- .../boost/unordered/unordered_flat_map.hpp | 23 +++++ include/boost/unordered/unordered_map.hpp | 95 +++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index ce09af90..0d38f145 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -337,6 +337,17 @@ namespace boost { return table_.try_emplace(std::move(key), std::forward(args)...); } + template + BOOST_FORCEINLINE typename std::enable_if< + boost::unordered::detail::transparent_non_iterable::value, + std::pair >::type + try_emplace(K&& key, Args&&... args) + { + return table_.try_emplace( + std::forward(key), std::forward(args)...); + } + template BOOST_FORCEINLINE iterator try_emplace( const_iterator, key_type const& key, Args&&... args) @@ -352,6 +363,18 @@ namespace boost { .first; } + template + BOOST_FORCEINLINE typename std::enable_if< + boost::unordered::detail::transparent_non_iterable::value, + iterator>::type + try_emplace(const_iterator, K&& key, Args&&... args) + { + return table_ + .try_emplace(std::forward(key), std::forward(args)...) + .first; + } + BOOST_FORCEINLINE void erase(iterator pos) { table_.erase(pos); } BOOST_FORCEINLINE void erase(const_iterator pos) { diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 9d80d743..3e07ed5e 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -486,6 +486,16 @@ namespace boost { boost::move(k), boost::forward(args)...); } + template + typename boost::enable_if_c< + detail::transparent_non_iterable::value, + std::pair >::type + try_emplace(Key&& k, Args&&... args) + { + return table_.try_emplace_unique( + boost::forward(k), boost::forward(args)...); + } + template iterator try_emplace( const_iterator hint, key_type const& k, BOOST_FWD_REF(Args)... args) @@ -502,6 +512,16 @@ namespace boost { hint, boost::move(k), boost::forward(args)...); } + template + typename boost::enable_if_c< + detail::transparent_non_iterable::value, + iterator>::type + try_emplace(const_iterator hint, Key&& k, Args&&... args) + { + return table_.try_emplace_hint_unique( + hint, boost::forward(k), boost::forward(args)...); + } + #else // In order to make this a template, this handles both: @@ -582,6 +602,43 @@ namespace boost { boost::forward(a1), boost::forward(a2))); } + // try_emplace(Key&&, Args&&...) + + template + typename boost::enable_if_c< + detail::transparent_non_iterable::value, + std::pair >::type + try_emplace(BOOST_FWD_REF(Key) k, BOOST_FWD_REF(A0) a0) + { + return table_.try_emplace_unique( + boost::forward(k), boost::unordered::detail::create_emplace_args( + boost::forward(a0))); + } + + template + typename boost::enable_if_c< + detail::transparent_non_iterable::value, + std::pair >::type + try_emplace( + BOOST_FWD_REF(Key) k, BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.try_emplace_unique(boost::forward(k), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + + template + typename boost::enable_if_c< + detail::transparent_non_iterable::value, + std::pair >::type + try_emplace(BOOST_FWD_REF(Key) k, BOOST_FWD_REF(A0) a0, + BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_.try_emplace_unique(boost::forward(k), + boost::unordered::detail::create_emplace_args(boost::forward(a0), + boost::forward(a1), boost::forward(a2))); + } + // try_emplace(const_iterator hint, key const&, Args&&...) template @@ -640,6 +697,44 @@ namespace boost { boost::forward(a1), boost::forward(a2))); } + // try_emplace(const_iterator hint, Key&&, Args&&...) + + template + typename boost::enable_if_c< + detail::transparent_non_iterable::value, + iterator>::type + try_emplace( + const_iterator hint, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(A0) a0) + { + return table_.try_emplace_hint_unique(hint, boost::forward(k), + boost::unordered::detail::create_emplace_args( + boost::forward(a0))); + } + + template + typename boost::enable_if_c< + detail::transparent_non_iterable::value, + iterator>::type + try_emplace(const_iterator hint, BOOST_FWD_REF(Key) k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1) + { + return table_.try_emplace_hint_unique(hint, boost::forward(k), + boost::unordered::detail::create_emplace_args( + boost::forward(a0), boost::forward(a1))); + } + + template + typename boost::enable_if_c< + detail::transparent_non_iterable::value, + iterator>::type + try_emplace(const_iterator hint, BOOST_FWD_REF(Key) k, + BOOST_FWD_REF(A0) a0, BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) + { + return table_.try_emplace_hint_unique(hint, boost::forward(k), + boost::unordered::detail::create_emplace_args(boost::forward(a0), + boost::forward(a1), boost::forward(a2))); + } + #define BOOST_UNORDERED_TRY_EMPLACE(z, n, _) \ \ template \ From 1ede59e662c1a7bcc2626bbdc587a69cc5eb57dd Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Tue, 15 Nov 2022 09:52:03 -0800 Subject: [PATCH 20/44] Improve naming in are_transparent --- include/boost/unordered/detail/type_traits.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/unordered/detail/type_traits.hpp b/include/boost/unordered/detail/type_traits.hpp index 3fe2f404..fd37a8e4 100644 --- a/include/boost/unordered/detail/type_traits.hpp +++ b/include/boost/unordered/detail/type_traits.hpp @@ -53,10 +53,10 @@ namespace boost { { }; - template struct are_transparent + template struct are_transparent { static bool const value = - is_transparent::value && is_transparent::value; + is_transparent::value && is_transparent::value; }; template struct transparent_non_iterable From 7709950111857fcdd94a237d65aeb5bd6bddb8f9 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Tue, 15 Nov 2022 09:52:37 -0800 Subject: [PATCH 21/44] Add transparent insert_or_assign() tests --- test/unordered/transparent_tests.cpp | 85 ++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index 8d74fc2a..9de531ae 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -1555,6 +1555,87 @@ template void test_map_non_transparent_try_emplace() BOOST_TEST(p == map.find(5)); } +template void test_map_transparent_insert_or_assign() +{ + count_reset(); + + typedef typename UnorderedMap::iterator iterator; + + UnorderedMap map; + + map.insert(std::make_pair(0, 1337)); + map.insert(std::make_pair(1, 1338)); + map.insert(std::make_pair(2, 1339)); + map.insert(std::make_pair(0, 1340)); + map.insert(std::make_pair(0, 1341)); + map.insert(std::make_pair(0, 1342)); + + int key_count = key::count_; + + std::pair r = map.insert_or_assign(0, 7331); + BOOST_TEST(r.first == map.find(0)); + BOOST_TEST_EQ(r.first->second, 7331); + BOOST_TEST_NOT(r.second); + BOOST_TEST_EQ(key::count_, key_count); + + r = map.insert_or_assign(4, 7331); + BOOST_TEST(r.first == map.find(4)); + BOOST_TEST(r.second); + BOOST_TEST_EQ(key::count_, key_count + 1); + + key_count = key::count_; + + iterator p = map.insert_or_assign(map.cbegin(), 0, 1111); + BOOST_TEST(p == map.find(0)); + BOOST_TEST_EQ(p->second, 1111); + BOOST_TEST_EQ(key::count_, key_count); + + p = map.insert_or_assign(map.begin(), 5, 7331); + BOOST_TEST(p == map.find(5)); + BOOST_TEST_EQ(key::count_, key_count + 1); +} + +template void test_map_non_transparent_insert_or_assign() +{ + count_reset(); + + typedef typename UnorderedMap::iterator iterator; + + UnorderedMap map; + + map.insert(std::make_pair(0, 1337)); + map.insert(std::make_pair(1, 1338)); + map.insert(std::make_pair(2, 1339)); + map.insert(std::make_pair(0, 1340)); + map.insert(std::make_pair(0, 1341)); + map.insert(std::make_pair(0, 1342)); + + int key_count = key::count_; + + std::pair r = map.insert_or_assign(0, 7331); + BOOST_TEST_EQ(key::count_, key_count + 1); + BOOST_TEST(r.first == map.find(0)); + BOOST_TEST_EQ(r.first->second, 7331); + BOOST_TEST_NOT(r.second); + + key_count = key::count_; + r = map.insert_or_assign(4, 7331); + BOOST_TEST_EQ(key::count_, key_count + 2); + BOOST_TEST(r.first == map.find(4)); + BOOST_TEST(r.second); + + key_count = key::count_; + iterator p = map.insert_or_assign(map.cbegin(), 0, 1111); + BOOST_TEST_EQ(key::count_, key_count + 1); + BOOST_TEST(p == map.find(0)); + BOOST_TEST_EQ(p->second, 1111); + + key_count = key::count_; + p = map.insert_or_assign(map.begin(), 5, 7331); + BOOST_TEST_EQ(key::count_, key_count + 2); + BOOST_TEST(p == map.find(5)); +} + #ifndef BOOST_UNORDERED_FOA_TESTS transparent_unordered_set::node_type set_extract_overload_compile_test() { @@ -1724,6 +1805,7 @@ void test_unordered_map() test_map_transparent_erase(); test_map_transparent_extract(); test_map_transparent_try_emplace(); + test_map_transparent_insert_or_assign(); } { @@ -1737,6 +1819,7 @@ void test_unordered_map() test_map_non_transparent_erase(); test_map_non_transparent_extract(); test_map_non_transparent_try_emplace(); + test_map_non_transparent_insert_or_assign(); } { @@ -1751,6 +1834,7 @@ void test_unordered_map() test_map_non_transparent_erase(); test_map_non_transparent_extract(); test_map_non_transparent_try_emplace(); + test_map_non_transparent_insert_or_assign(); } { @@ -1765,6 +1849,7 @@ void test_unordered_map() test_map_non_transparent_erase(); test_map_non_transparent_extract(); test_map_non_transparent_try_emplace(); + test_map_non_transparent_insert_or_assign(); } } From 01d508b6affd1977f36cad192a6baa603082bd27 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Tue, 15 Nov 2022 09:53:02 -0800 Subject: [PATCH 22/44] Improve naming in insert_or_assign() --- .../boost/unordered/unordered_flat_map.hpp | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index 0d38f145..4c9a845b 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -278,24 +278,25 @@ namespace boost { template std::pair insert_or_assign(key_type const& key, M&& obj) { - auto iter_bool_pair = table_.try_emplace(key, std::forward(obj)); - if (iter_bool_pair.second) { - return iter_bool_pair; + auto ibp = table_.try_emplace(key, std::forward(obj)); + if (ibp.second) { + return ibp; } - iter_bool_pair.first->second = std::forward(obj); - return iter_bool_pair; + ibp.first->second = std::forward(obj); + return ibp; } template std::pair insert_or_assign(key_type&& key, M&& obj) { - auto iter_bool_pair = + auto ibp = table_.try_emplace(std::move(key), std::forward(obj)); - if (iter_bool_pair.second) { - return iter_bool_pair; + if (ibp.second) { + return ibp; } - iter_bool_pair.first->second = std::forward(obj); - return iter_bool_pair; + ibp.first->second = std::forward(obj); + return ibp; + } } template From 0a879c10632248363232bc5cc1c71104ae274da0 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Tue, 15 Nov 2022 09:53:18 -0800 Subject: [PATCH 23/44] Implement transparent insert_or_assign() --- .../boost/unordered/unordered_flat_map.hpp | 26 +++++++++++++++++-- include/boost/unordered/unordered_map.hpp | 22 ++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index 4c9a845b..16312a9e 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -289,14 +289,26 @@ namespace boost { template std::pair insert_or_assign(key_type&& key, M&& obj) { - auto ibp = - table_.try_emplace(std::move(key), std::forward(obj)); + auto ibp = table_.try_emplace(std::move(key), std::forward(obj)); if (ibp.second) { return ibp; } ibp.first->second = std::forward(obj); return ibp; } + + template + typename std::enable_if< + boost::unordered::detail::are_transparent::value, + std::pair >::type + insert_or_assign(K&& k, M&& obj) + { + auto ibp = table_.try_emplace(std::forward(k), std::forward(obj)); + if (ibp.second) { + return ibp; + } + ibp.first->second = std::forward(obj); + return ibp; } template @@ -312,6 +324,16 @@ namespace boost { .first; } + template + typename std::enable_if< + boost::unordered::detail::are_transparent::value, + iterator>::type + insert_or_assign(const_iterator, K&& k, M&& obj) + { + return this->insert_or_assign(std::forward(k), std::forward(obj)) + .first; + } + template BOOST_FORCEINLINE std::pair emplace(Args&&... args) { diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 3e07ed5e..6d81a3f8 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -801,6 +801,16 @@ namespace boost { boost::move(k), boost::forward(obj)); } + template + typename boost::enable_if_c< + detail::are_transparent::value, + std::pair >::type + insert_or_assign(BOOST_FWD_REF(Key) k, BOOST_FWD_REF(M) obj) + { + return table_.insert_or_assign_unique( + boost::forward(k), boost::forward(obj)); + } + template iterator insert_or_assign( const_iterator, key_type const& k, BOOST_FWD_REF(M) obj) @@ -817,6 +827,18 @@ namespace boost { .first; } + template + typename boost::enable_if_c< + detail::are_transparent::value, iterator>::type + insert_or_assign( + const_iterator, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(M) obj) + { + return table_ + .insert_or_assign_unique( + boost::forward(k), boost::forward(obj)) + .first; + } + iterator erase(iterator); iterator erase(const_iterator); size_type erase(const key_type&); From b85e17085fc5c695102b7a24643893af3678acff Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Tue, 15 Nov 2022 15:38:02 -0800 Subject: [PATCH 24/44] Add tests for transparent subscript operator --- test/unordered/transparent_tests.cpp | 49 ++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index 9de531ae..03c8d2f8 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -1636,6 +1636,51 @@ template void test_map_non_transparent_insert_or_assign() BOOST_TEST(p == map.find(5)); } +template void test_map_transparent_subscript() +{ + count_reset(); + + UnorderedMap map; + + map[0] = 1337; + map[1] = 1338; + map[2] = 1339; + map[0] = 1340; + map[0] = 1341; + map[0] = 1342; + + int key_count = key::count_; + + map[0] = 7331; + BOOST_ASSERT(BOOST_TEST_EQ(key::count_, key_count)); + + map[4] = 7331; + BOOST_TEST_EQ(key::count_, key_count + 1); +} + +template void test_map_non_transparent_subscript() +{ + count_reset(); + + UnorderedMap map; + + map[0] = 1337; + map[1] = 1338; + map[2] = 1339; + map[0] = 1340; + map[0] = 1341; + map[0] = 1342; + + int key_count = key::count_; + + map[0] = 7331; + BOOST_ASSERT(BOOST_TEST_EQ(key::count_, key_count + 1)); + + key_count = key::count_; + map[4] = 7331; + BOOST_TEST_EQ(key::count_, key_count + 2); +} + #ifndef BOOST_UNORDERED_FOA_TESTS transparent_unordered_set::node_type set_extract_overload_compile_test() { @@ -1806,6 +1851,7 @@ void test_unordered_map() test_map_transparent_extract(); test_map_transparent_try_emplace(); test_map_transparent_insert_or_assign(); + test_map_transparent_subscript(); } { @@ -1820,6 +1866,7 @@ void test_unordered_map() test_map_non_transparent_extract(); test_map_non_transparent_try_emplace(); test_map_non_transparent_insert_or_assign(); + test_map_non_transparent_subscript(); } { @@ -1835,6 +1882,7 @@ void test_unordered_map() test_map_non_transparent_extract(); test_map_non_transparent_try_emplace(); test_map_non_transparent_insert_or_assign(); + test_map_non_transparent_subscript(); } { @@ -1850,6 +1898,7 @@ void test_unordered_map() test_map_non_transparent_extract(); test_map_non_transparent_try_emplace(); test_map_non_transparent_insert_or_assign(); + test_map_non_transparent_subscript(); } } From dfbff823a9397dfeb8dcee42c1175b28e8cb6f93 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Tue, 15 Nov 2022 15:39:15 -0800 Subject: [PATCH 25/44] Add transparent subscript --- include/boost/unordered/unordered_flat_map.hpp | 9 +++++++++ include/boost/unordered/unordered_map.hpp | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index 16312a9e..d7f4e6e7 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -483,6 +483,15 @@ namespace boost { return table_.try_emplace(std::move(key)).first->second; } + template + typename std::enable_if< + boost::unordered::detail::are_transparent::value, + mapped_type&>::type + operator[](K&& key) + { + return table_.try_emplace(std::forward(key)).first->second; + } + BOOST_FORCEINLINE size_type count(key_type const& key) const { auto pos = table_.find(key); diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 6d81a3f8..315da4cc 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -973,6 +973,10 @@ namespace boost { mapped_type& operator[](const key_type&); mapped_type& operator[](BOOST_RV_REF(key_type)); + template + typename boost::enable_if_c::value, + mapped_type&>::type + operator[](BOOST_FWD_REF(Key) k); mapped_type& at(const key_type&); mapped_type const& at(const key_type&) const; @@ -2215,6 +2219,15 @@ namespace boost { return table_.try_emplace_unique(boost::move(k)).first->second; } + template + template + typename boost::enable_if_c::value, + typename unordered_map::mapped_type&>::type + unordered_map::operator[](BOOST_FWD_REF(Key) k) + { + return table_.try_emplace_unique(boost::forward(k)).first->second; + } + template typename unordered_map::mapped_type& unordered_map::at(const key_type& k) From 1f4244ec275f52fd8cf275099fdd232e9c256a2b Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Wed, 16 Nov 2022 13:30:35 -0800 Subject: [PATCH 26/44] Add tests for transparent at() --- test/unordered/transparent_tests.cpp | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index 03c8d2f8..effe08b5 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -1681,6 +1681,67 @@ template void test_map_non_transparent_subscript() BOOST_TEST_EQ(key::count_, key_count + 2); } +template void test_map_transparent_at() +{ + count_reset(); + + UnorderedMap map; + + map.insert(std::make_pair(0, 1337)); + map.insert(std::make_pair(1, 1338)); + map.insert(std::make_pair(2, 1339)); + map.insert(std::make_pair(0, 1340)); + map.insert(std::make_pair(0, 1341)); + map.insert(std::make_pair(0, 1342)); + + int key_count = key::count_; + + map.at(0) = 7331; + BOOST_TEST_EQ(key::count_, key_count); + + BOOST_TEST_THROWS(map.at(4), std::out_of_range); + BOOST_TEST_EQ(key::count_, key_count); + + UnorderedMap const& m = map; + BOOST_TEST_EQ(m.at(0), 7331); + BOOST_TEST_EQ(key::count_, key_count); + + BOOST_TEST_THROWS(m.at(4), std::out_of_range); + BOOST_TEST_EQ(key::count_, key_count); +} + +template void test_map_non_transparent_at() +{ + count_reset(); + + UnorderedMap map; + + map.insert(std::make_pair(0, 1337)); + map.insert(std::make_pair(1, 1338)); + map.insert(std::make_pair(2, 1339)); + map.insert(std::make_pair(0, 1340)); + map.insert(std::make_pair(0, 1341)); + map.insert(std::make_pair(0, 1342)); + + int key_count = key::count_; + + map.at(0) = 7331; + BOOST_TEST_EQ(key::count_, key_count + 1); + + key_count = key::count_; + BOOST_TEST_THROWS(map.at(4), std::out_of_range); + BOOST_TEST_EQ(key::count_, key_count + 1); + + key_count = key::count_; + UnorderedMap const& m = map; + BOOST_TEST_EQ(m.at(0), 7331); + BOOST_TEST_EQ(key::count_, key_count + 1); + + key_count = key::count_; + BOOST_TEST_THROWS(m.at(4), std::out_of_range); + BOOST_TEST_EQ(key::count_, key_count + 1); +} + #ifndef BOOST_UNORDERED_FOA_TESTS transparent_unordered_set::node_type set_extract_overload_compile_test() { @@ -1852,6 +1913,7 @@ void test_unordered_map() test_map_transparent_try_emplace(); test_map_transparent_insert_or_assign(); test_map_transparent_subscript(); + test_map_transparent_at(); } { @@ -1867,6 +1929,7 @@ void test_unordered_map() test_map_non_transparent_try_emplace(); test_map_non_transparent_insert_or_assign(); test_map_non_transparent_subscript(); + test_map_non_transparent_at(); } { @@ -1883,6 +1946,7 @@ void test_unordered_map() test_map_non_transparent_try_emplace(); test_map_non_transparent_insert_or_assign(); test_map_non_transparent_subscript(); + test_map_non_transparent_at(); } { @@ -1899,6 +1963,7 @@ void test_unordered_map() test_map_non_transparent_try_emplace(); test_map_non_transparent_insert_or_assign(); test_map_non_transparent_subscript(); + test_map_non_transparent_at(); } } From 0e980577b0345bb516a436a5d913b8ed23083939 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Wed, 16 Nov 2022 14:00:22 -0800 Subject: [PATCH 27/44] Add transparent at() --- .../boost/unordered/unordered_flat_map.hpp | 28 +++++++++++ include/boost/unordered/unordered_map.hpp | 46 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index d7f4e6e7..16d4f3bd 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -473,6 +473,34 @@ namespace boost { std::out_of_range("key was not found in unordered_flat_map")); } + template + typename std::enable_if< + boost::unordered::detail::are_transparent::value, + mapped_type&>::type + at(K&& key) + { + auto pos = table_.find(std::forward(key)); + if (pos != table_.end()) { + return pos->second; + } + boost::throw_exception( + std::out_of_range("key was not found in unordered_flat_map")); + } + + template + typename std::enable_if< + boost::unordered::detail::are_transparent::value, + mapped_type const&>::type + at(K&& key) const + { + auto pos = table_.find(std::forward(key)); + if (pos != table_.end()) { + return pos->second; + } + boost::throw_exception( + std::out_of_range("key was not found in unordered_flat_map")); + } + BOOST_FORCEINLINE mapped_type& operator[](key_type const& key) { return table_.try_emplace(key).first->second; diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 315da4cc..849adefa 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -973,13 +973,23 @@ namespace boost { mapped_type& operator[](const key_type&); mapped_type& operator[](BOOST_RV_REF(key_type)); + template typename boost::enable_if_c::value, mapped_type&>::type operator[](BOOST_FWD_REF(Key) k); + mapped_type& at(const key_type&); mapped_type const& at(const key_type&) const; + template + typename boost::enable_if_c::value, + mapped_type&>::type at(BOOST_FWD_REF(Key) k); + + template + typename boost::enable_if_c::value, + mapped_type const&>::type at(BOOST_FWD_REF(Key) k) const; + // bucket interface size_type bucket_count() const BOOST_NOEXCEPT @@ -2260,6 +2270,42 @@ namespace boost { std::out_of_range("Unable to find key in unordered_map.")); } + template + template + typename boost::enable_if_c::value, + typename unordered_map::mapped_type&>::type + unordered_map::at(BOOST_FWD_REF(Key) k) + { + typedef typename table::node_pointer node_pointer; + + if (table_.size_) { + node_pointer p = table_.find_node(boost::forward(k)); + if (p) + return p->value().second; + } + + boost::throw_exception( + std::out_of_range("Unable to find key in unordered_map.")); + } + + template + template + typename boost::enable_if_c::value, + typename unordered_map::mapped_type const&>::type + unordered_map::at(BOOST_FWD_REF(Key) k) const + { + typedef typename table::node_pointer node_pointer; + + if (table_.size_) { + node_pointer p = table_.find_node(boost::forward(k)); + if (p) + return p->value().second; + } + + boost::throw_exception( + std::out_of_range("Unable to find key in unordered_map.")); + } + template typename unordered_map::size_type unordered_map::bucket_size(size_type n) const From 8b1dcd3da3ad6dd8014b9801c7f7d0a5f324a5c9 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 17 Nov 2022 11:38:57 -0800 Subject: [PATCH 28/44] Add tests for transparent bucket() --- test/unordered/transparent_tests.cpp | 100 +++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index effe08b5..aaa9b78f 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -1742,6 +1742,48 @@ template void test_map_non_transparent_at() BOOST_TEST_EQ(key::count_, key_count + 1); } +template void test_map_transparent_bucket() +{ +#ifndef BOOST_UNORDERED_FOA_TESTS + count_reset(); + + UnorderedMap map; + + map.insert(std::make_pair(0, 1337)); + map.insert(std::make_pair(1, 1338)); + map.insert(std::make_pair(2, 1339)); + map.insert(std::make_pair(0, 1340)); + map.insert(std::make_pair(0, 1341)); + map.insert(std::make_pair(0, 1342)); + + int key_count = key::count_; + map.bucket(0); + map.bucket(4); + BOOST_TEST_EQ(key::count_, key_count); +#endif +} + +template void test_map_non_transparent_bucket() +{ +#ifndef BOOST_UNORDERED_FOA_TESTS + count_reset(); + + UnorderedMap map; + + map.insert(std::make_pair(0, 1337)); + map.insert(std::make_pair(1, 1338)); + map.insert(std::make_pair(2, 1339)); + map.insert(std::make_pair(0, 1340)); + map.insert(std::make_pair(0, 1341)); + map.insert(std::make_pair(0, 1342)); + + int key_count = key::count_; + map.bucket(0); + map.bucket(4); + BOOST_TEST_EQ(key::count_, key_count + 2); +#endif +} + #ifndef BOOST_UNORDERED_FOA_TESTS transparent_unordered_set::node_type set_extract_overload_compile_test() { @@ -1890,6 +1932,48 @@ template void test_set_non_transparent_extract() #endif } +template void test_set_transparent_bucket() +{ +#ifndef BOOST_UNORDERED_FOA_TESTS + count_reset(); + + UnorderedSet set; + + set.insert(0); + set.insert(1); + set.insert(2); + set.insert(0); + set.insert(0); + set.insert(0); + + int key_count = key::count_; + set.bucket(0); + set.bucket(4); + BOOST_TEST_EQ(key::count_, key_count); +#endif +} + +template void test_set_non_transparent_bucket() +{ +#ifndef BOOST_UNORDERED_FOA_TESTS + count_reset(); + + UnorderedSet set; + + set.insert(0); + set.insert(1); + set.insert(2); + set.insert(0); + set.insert(0); + set.insert(0); + + int key_count = key::count_; + set.bucket(0); + set.bucket(4); + BOOST_TEST_EQ(key::count_, key_count + 2); +#endif +} + template struct map_type { #ifdef BOOST_UNORDERED_FOA_TESTS @@ -1914,6 +1998,7 @@ void test_unordered_map() test_map_transparent_insert_or_assign(); test_map_transparent_subscript(); test_map_transparent_at(); + test_map_transparent_bucket(); } { @@ -1930,6 +2015,7 @@ void test_unordered_map() test_map_non_transparent_insert_or_assign(); test_map_non_transparent_subscript(); test_map_non_transparent_at(); + test_map_non_transparent_bucket(); } { @@ -1947,6 +2033,7 @@ void test_unordered_map() test_map_non_transparent_insert_or_assign(); test_map_non_transparent_subscript(); test_map_non_transparent_at(); + test_map_non_transparent_bucket(); } { @@ -1964,6 +2051,7 @@ void test_unordered_map() test_map_non_transparent_insert_or_assign(); test_map_non_transparent_subscript(); test_map_non_transparent_at(); + test_map_non_transparent_bucket(); } } @@ -1980,6 +2068,7 @@ void test_unordered_multimap() test_map_transparent_equal_range(); test_map_transparent_erase(); test_map_transparent_extract(); + test_map_transparent_bucket(); } { @@ -1993,6 +2082,7 @@ void test_unordered_multimap() test_map_non_transparent_equal_range(); test_map_non_transparent_erase(); test_map_non_transparent_extract(); + test_map_non_transparent_bucket(); } { @@ -2006,6 +2096,7 @@ void test_unordered_multimap() test_map_non_transparent_equal_range(); test_map_non_transparent_erase(); test_map_non_transparent_extract(); + test_map_non_transparent_bucket(); } { @@ -2019,6 +2110,7 @@ void test_unordered_multimap() test_map_non_transparent_equal_range(); test_map_non_transparent_erase(); test_map_non_transparent_extract(); + test_map_non_transparent_bucket(); } } #endif @@ -2043,6 +2135,7 @@ void test_unordered_set() test_set_transparent_erase(); test_set_transparent_equal_range(); test_set_transparent_extract(); + test_set_transparent_bucket(); } { @@ -2055,6 +2148,7 @@ void test_unordered_set() test_set_non_transparent_erase(); test_set_non_transparent_equal_range(); test_set_non_transparent_extract(); + test_set_non_transparent_bucket(); } { @@ -2067,6 +2161,7 @@ void test_unordered_set() test_set_non_transparent_erase(); test_set_non_transparent_equal_range(); test_set_non_transparent_extract(); + test_set_non_transparent_bucket(); } { @@ -2079,6 +2174,7 @@ void test_unordered_set() test_set_non_transparent_erase(); test_set_non_transparent_equal_range(); test_set_non_transparent_extract(); + test_set_non_transparent_bucket(); } } @@ -2095,6 +2191,7 @@ void test_unordered_multiset() test_set_transparent_erase(); test_set_transparent_equal_range(); test_set_transparent_extract(); + test_set_transparent_bucket(); } { @@ -2107,6 +2204,7 @@ void test_unordered_multiset() test_set_non_transparent_erase(); test_set_non_transparent_equal_range(); test_set_non_transparent_extract(); + test_set_non_transparent_bucket(); } { @@ -2120,6 +2218,7 @@ void test_unordered_multiset() test_set_non_transparent_erase(); test_set_non_transparent_equal_range(); test_set_non_transparent_extract(); + test_set_non_transparent_bucket(); } { @@ -2133,6 +2232,7 @@ void test_unordered_multiset() test_set_non_transparent_erase(); test_set_non_transparent_equal_range(); test_set_non_transparent_extract(); + test_set_non_transparent_bucket(); } } #endif From 6c60524fb7878545760a5f16471fe82fbe4d316a Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 17 Nov 2022 11:39:06 -0800 Subject: [PATCH 29/44] Implement transparent bucket() --- include/boost/unordered/unordered_map.hpp | 23 +++++++++++++++++++---- include/boost/unordered/unordered_set.hpp | 16 ++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 849adefa..6d05471c 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -802,8 +802,7 @@ namespace boost { } template - typename boost::enable_if_c< - detail::are_transparent::value, + typename boost::enable_if_c::value, std::pair >::type insert_or_assign(BOOST_FWD_REF(Key) k, BOOST_FWD_REF(M) obj) { @@ -828,8 +827,8 @@ namespace boost { } template - typename boost::enable_if_c< - detail::are_transparent::value, iterator>::type + typename boost::enable_if_c::value, + iterator>::type insert_or_assign( const_iterator, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(M) obj) { @@ -1009,6 +1008,14 @@ namespace boost { return table_.hash_to_bucket(table_.hash(k)); } + template + typename boost::enable_if_c::value, + size_type>::type + bucket(BOOST_FWD_REF(Key) k) const + { + return table_.hash_to_bucket(table_.hash(boost::forward(k))); + } + local_iterator begin(size_type n) { return table_.begin(n); @@ -1715,6 +1722,14 @@ namespace boost { return table_.hash_to_bucket(table_.hash(k)); } + template + typename boost::enable_if_c::value, + size_type>::type + bucket(BOOST_FWD_REF(Key) k) const + { + return table_.hash_to_bucket(table_.hash(boost::forward(k))); + } + local_iterator begin(size_type n) { return local_iterator(table_.begin(n)); diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index a054ed64..9542b492 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -571,6 +571,14 @@ namespace boost { return table_.hash_to_bucket(table_.hash(k)); } + template + typename boost::enable_if_c::value, + size_type>::type + bucket(BOOST_FWD_REF(Key) k) const + { + return table_.hash_to_bucket(table_.hash(boost::forward(k))); + } + local_iterator begin(size_type n) { return local_iterator(table_.begin(n)); @@ -1212,6 +1220,14 @@ namespace boost { return table_.hash_to_bucket(table_.hash(k)); } + template + typename boost::enable_if_c::value, + size_type>::type + bucket(BOOST_FWD_REF(Key) k) const + { + return table_.hash_to_bucket(table_.hash(boost::forward(k))); + } + local_iterator begin(size_type n) { return local_iterator(table_.begin(n)); From 8b14b7cddc0c67cdb9d5564b37f4836deb53ca14 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 8 Dec 2022 12:25:27 -0800 Subject: [PATCH 30/44] Add tests for narrow_cast --- test/Jamfile.v2 | 1 + test/unordered/narrow_cast_tests.cpp | 107 +++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 test/unordered/narrow_cast_tests.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index c3284098..9a702bc6 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -95,6 +95,7 @@ run exception/rehash_exception_tests.cpp ; run exception/swap_exception_tests.cpp : : : BOOST_UNORDERED_SWAP_METHOD=2 ; run exception/merge_exception_tests.cpp ; +run unordered/narrow_cast_tests.cpp ; run quick.cpp ; import ../../config/checks/config : requires ; diff --git a/test/unordered/narrow_cast_tests.cpp b/test/unordered/narrow_cast_tests.cpp new file mode 100644 index 00000000..81d5ee82 --- /dev/null +++ b/test/unordered/narrow_cast_tests.cpp @@ -0,0 +1,107 @@ +// Copyright 2022 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#include +#include + +// want to prove that for the wider type, the higher bits of the value +// represenation don't affect the results of the narrowing, which in this case +// is masking out the high bits when comapred to the narrow type + +static void signed_integral_narrowing() +{ + // test positive range, fits + // [0, 127] + for (boost::int32_t i = 0x00; i < 0x80; ++i) { + boost::int8_t k = (boost::int8_t)i; + BOOST_TEST_GE(k, 0); + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(i), k); + } + + // test positive range, doesn't fit + // [0xff00, 0xff7f] + for (boost::int32_t i = 0x00; i < 0x80; ++i) { + boost::int32_t j = i + 0xff00; + boost::int8_t k = (boost::int8_t)i; + BOOST_TEST_GE(k, 0); + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(j), k); + } + + // test negative range, fits + // [-128, -1] + for (boost::int32_t i = 0x00; i < 0x80; ++i) { + boost::int32_t j = i + (boost::int32_t)0xffffff80; + boost::int8_t k = (boost::int8_t)j; + BOOST_TEST_LT(j, 0); + BOOST_TEST_LT(k, 0); + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(j), k); + } + + // test negative range, doesn't fit + for (boost::int32_t i = 0x00; i < 0x80; ++i) { + boost::int32_t j = i + (boost::int32_t)0x80000000; + boost::int8_t k = (boost::int8_t)(i); + BOOST_TEST_LT(j, 0); + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(j), k); + } + + for (boost::int32_t i = 0x00; i < 0x100; ++i) { + boost::int32_t j = (boost::int32_t)0x80ff0000 + i; + BOOST_TEST_LT(j, 0); + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(j), + (boost::int8_t)i); + } + + // test special values + { + boost::int32_t x = 0xff; + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(x), -1); + } + + { + boost::int32_t x = (boost::int32_t)0xffffff00; + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(x), + (boost::int8_t)0x00); + } + + { + boost::int32_t x = (boost::int32_t)0xffffff7f; + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(x), + (boost::int8_t)0x7f); + } + + { + boost::int32_t x = (boost::int32_t)0xffffffff; + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(x), + (boost::int8_t)-1); + } +} + +static void unsigned_integral_narrowing() +{ + // test range: [0x00, 0xff] + for (boost::uint32_t i = 0x00; i < 0x100; ++i) { + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(i), + (boost::uint8_t)(i & 0xff)); + } + + // test range: [0xffffff00, 0xffffffff] + boost::uint32_t i = 0xffffff00; + for (; i < 0xffffffff; ++i) { + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(i), + (boost::uint8_t)(i & 0xff)); + } + BOOST_TEST_EQ(boost::unordered::detail::narrow_cast(i), + (boost::uint8_t)(i & 0xff)); +} + +int main() +{ + signed_integral_narrowing(); + unsigned_integral_narrowing(); + + return boost::report_errors(); +} From f0037d336dc2b520705b96ccc675d2343eb3643c Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 5 Dec 2022 11:50:18 +0100 Subject: [PATCH 31/44] added narrow_cast --- include/boost/unordered/detail/foa.hpp | 31 +++---------- .../boost/unordered/detail/narrow_cast.hpp | 44 +++++++++++++++++++ include/boost/unordered/detail/prime_fmod.hpp | 9 +--- 3 files changed, 52 insertions(+), 32 deletions(-) create mode 100644 include/boost/unordered/detail/narrow_cast.hpp diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 41a1f153..ebeed0f7 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -269,20 +270,12 @@ private: 0xF8F8F8F8u,0xF9F9F9F9u,0xFAFAFAFAu,0xFBFBFBFBu,0xFCFCFCFCu,0xFDFDFDFDu,0xFEFEFEFEu,0xFFFFFFFFu, }; -#if defined(__MSVC_RUNTIME_CHECKS) - return (int)word[hash&0xffu]; -#else - return (int)word[(unsigned char)hash]; -#endif + return (int)word[narrow_cast(hash)]; } inline static unsigned char reduced_hash(std::size_t hash) { -#if defined(__MSVC_RUNTIME_CHECKS) - return match_word(hash)&0xffu; -#else - return (unsigned char)match_word(hash); -#endif + return narrow_cast(match_word(hash)); } inline unsigned char& at(std::size_t pos) @@ -533,11 +526,7 @@ struct group15 std::size_t pos=reinterpret_cast(pc)%sizeof(group15); group15 *pg=reinterpret_cast(pc-pos); boost::uint64_t x=((pg->m[0])>>pos)&0x000100010001ull; -#if defined(__MSVC_RUNTIME_CHECKS) - boost::uint32_t y=(x|(x>>15)|(x>>30))&0xffffffffu; -#else - boost::uint32_t y=static_cast(x|(x>>15)|(x>>30)); -#endif + boost::uint32_t y=narrow_cast(x|(x>>15)|(x>>30)); return !pg->is_not_overflowed(y); }; @@ -552,11 +541,7 @@ struct group15 inline int match_occupied()const { boost::uint64_t x=m[0]|m[1]; -#if defined(__MSVC_RUNTIME_CHECKS) - boost::uint32_t y=(x|(x>>32))&0xffffffffu; -#else - boost::uint32_t y=static_cast(x|(x>>32)); -#endif + boost::uint32_t y=narrow_cast(x|(x>>32)); y|=y>>16; return y&0x7FFF; } @@ -591,11 +576,7 @@ private: 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, }; -#if defined(__MSVC_RUNTIME_CHECKS) - return table[hash&0xffu]; -#else - return table[(unsigned char)hash]; -#endif + return table[narrow_cast(hash)]; } inline void set_impl(std::size_t pos,std::size_t n) diff --git a/include/boost/unordered/detail/narrow_cast.hpp b/include/boost/unordered/detail/narrow_cast.hpp new file mode 100644 index 00000000..da89f67f --- /dev/null +++ b/include/boost/unordered/detail/narrow_cast.hpp @@ -0,0 +1,44 @@ +/* Copyright 2022 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_NARROW_CAST_HPP +#define BOOST_UNORDERED_DETAIL_NARROW_CAST_HPP + +#include +#include +#include +#include + +namespace boost{ +namespace unordered{ +namespace detail{ + +template +BOOST_CONSTEXPR To narrow_cast(From x) BOOST_NOEXCEPT +{ + BOOST_STATIC_ASSERT(boost::is_integral::value); + BOOST_STATIC_ASSERT(boost::is_integral::value); + BOOST_STATIC_ASSERT(sizeof(From)>=sizeof(To)); + + return static_cast( + x + +#if defined(__MSVC_RUNTIME_CHECKS) + /* Avoids VS's "Run-Time Check Failure #1 - A cast to a smaller data type + * has caused a loss of data." + */ + &static_cast::type>(~static_cast(0)) +#endif + ); +} + +} /* namespace detail */ +} /* namespace unordered */ +} /* namespace boost */ + +#endif diff --git a/include/boost/unordered/detail/prime_fmod.hpp b/include/boost/unordered/detail/prime_fmod.hpp index 4c538487..fab8f94e 100644 --- a/include/boost/unordered/detail/prime_fmod.hpp +++ b/include/boost/unordered/detail/prime_fmod.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -117,15 +118,9 @@ namespace boost { #if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) std::size_t sizes_under_32bit = inv_sizes32_len; if (BOOST_LIKELY(size_index < sizes_under_32bit)) { -#if defined(__MSVC_RUNTIME_CHECKS) return fast_modulo( - boost::uint32_t(hash & 0xffffffffu) + boost::uint32_t(hash >> 32), + narrow_cast(hash) + narrow_cast(hash >> 32), inv_sizes32[size_index], boost::uint32_t(sizes[size_index])); -#else - return fast_modulo( - boost::uint32_t(hash) + boost::uint32_t(hash >> 32), - inv_sizes32[size_index], boost::uint32_t(sizes[size_index])); -#endif } else { return positions[size_index - sizes_under_32bit](hash); } From 7572de875c56fc8e5f7eab67188f24672986e95c Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Tue, 22 Nov 2022 15:38:41 -0800 Subject: [PATCH 32/44] Add transparent insert tests for set containers --- test/unordered/transparent_tests.cpp | 90 ++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index aaa9b78f..8d1f781b 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -1974,6 +1974,92 @@ template void test_set_non_transparent_bucket() #endif } +template void test_set_transparent_insert() +{ + count_reset(); + + typedef typename UnorderedSet::iterator iterator; + + UnorderedSet set; + + set.insert(0); + set.insert(1); + set.insert(2); + set.insert(0); + set.insert(0); + set.insert(0); + + int key_count = key::count_; + + std::pair p = set.insert(0); + BOOST_TEST(p.first == set.find(0)); + BOOST_TEST_NOT(p.second); + BOOST_TEST_EQ(key::count_, key_count); + + key_count = key::count_; + p = set.insert(4); + BOOST_TEST(p.first == set.find(4)); + BOOST_TEST(p.second); + BOOST_TEST_EQ(key::count_, key_count + 1); + + key_count = key::count_; + iterator pos = set.insert(set.begin(), 0); + BOOST_TEST(pos == set.find(0)); + BOOST_TEST_EQ(key::count_, key_count); + + key_count = key::count_; + pos = set.insert(set.begin(), 5); + BOOST_TEST(pos == set.find(5)); + BOOST_TEST_EQ(key::count_, key_count + 1); + + // check for collisions with insert(iterator, iterator) + // note: this precludes Key from being convertible to an iterator which isn't + // explicitly stated by p2363r3 + // + set.insert(set.begin(), set.end()); +} + +template void test_set_non_transparent_insert() +{ + count_reset(); + + typedef typename UnorderedSet::iterator iterator; + + UnorderedSet set; + + set.insert(0); + set.insert(1); + set.insert(2); + set.insert(0); + set.insert(0); + set.insert(0); + + int key_count = key::count_; + + std::pair p = set.insert(0); + BOOST_TEST_EQ(key::count_, key_count + 1); + BOOST_TEST(p.first == set.find(0)); + BOOST_TEST_NOT(p.second); + + key_count = key::count_; + p = set.insert(4); + BOOST_TEST_EQ(key::count_, key_count + 2); + BOOST_TEST(p.first == set.find(4)); + BOOST_TEST(p.second); + + key_count = key::count_; + iterator pos = set.insert(set.begin(), 0); + BOOST_TEST_EQ(key::count_, key_count + 1); + BOOST_TEST(pos == set.find(0)); + + key_count = key::count_; + pos = set.insert(set.begin(), 5); + BOOST_TEST_EQ(key::count_, key_count + 2); + BOOST_TEST(pos == set.find(5)); + + set.insert(set.begin(), set.end()); +} + template struct map_type { #ifdef BOOST_UNORDERED_FOA_TESTS @@ -2136,6 +2222,7 @@ void test_unordered_set() test_set_transparent_equal_range(); test_set_transparent_extract(); test_set_transparent_bucket(); + test_set_transparent_insert(); } { @@ -2149,6 +2236,7 @@ void test_unordered_set() test_set_non_transparent_equal_range(); test_set_non_transparent_extract(); test_set_non_transparent_bucket(); + test_set_non_transparent_insert(); } { @@ -2162,6 +2250,7 @@ void test_unordered_set() test_set_non_transparent_equal_range(); test_set_non_transparent_extract(); test_set_non_transparent_bucket(); + test_set_non_transparent_insert(); } { @@ -2175,6 +2264,7 @@ void test_unordered_set() test_set_non_transparent_equal_range(); test_set_non_transparent_extract(); test_set_non_transparent_bucket(); + test_set_non_transparent_insert(); } } From 7d77f1d4788dc9d82572e734548674167c31919c Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Tue, 13 Dec 2022 12:33:07 -0800 Subject: [PATCH 33/44] Add transparent insert() overloads to unordered_[flat]_set --- .../boost/unordered/detail/implementation.hpp | 25 ++++++++++++++++--- .../boost/unordered/unordered_flat_set.hpp | 18 +++++++++++++ include/boost/unordered/unordered_set.hpp | 18 +++++++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index d953b313..13351171 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1477,7 +1477,22 @@ namespace boost { } #endif - } + + template + inline typename boost::allocator_pointer::type + construct_node_from_key(T*, Alloc& alloc, BOOST_FWD_REF(Key) k) + { + return construct_node(alloc, boost::forward(k)); + } + + template + inline typename boost::allocator_pointer::type + construct_node_from_key( + std::pair*, Alloc& alloc, BOOST_FWD_REF(Key) k) + { + return construct_node_pair(alloc, boost::forward(k)); + } + } // namespace func } } } @@ -2640,8 +2655,10 @@ namespace boost { } else { node_allocator_type alloc = node_alloc(); - node_tmp tmp( - detail::func::construct_node_pair(alloc, boost::forward(k)), + value_type* dispatch = BOOST_NULLPTR; + + node_tmp tmp(detail::func::construct_node_from_key( + dispatch, alloc, boost::forward(k)), alloc); if (size_ + 1 > max_load_) { @@ -2660,7 +2677,7 @@ namespace boost { template iterator try_emplace_hint_unique(c_iterator hint, BOOST_FWD_REF(Key) k) { - if (hint.p && this->key_eq()(hint->first, k)) { + if (hint.p && this->key_eq()(extractor::extract(*hint), k)) { return iterator(hint.p, hint.itb); } else { return try_emplace_unique(k).first; diff --git a/include/boost/unordered/unordered_flat_set.hpp b/include/boost/unordered/unordered_flat_set.hpp index 29c53596..1599ba55 100644 --- a/include/boost/unordered/unordered_flat_set.hpp +++ b/include/boost/unordered/unordered_flat_set.hpp @@ -231,6 +231,15 @@ namespace boost { return table_.insert(std::move(value)); } + template + BOOST_FORCEINLINE typename std::enable_if< + detail::transparent_non_iterable::value, + std::pair >::type + insert(K&& k) + { + return table_.try_emplace(std::forward(k)); + } + BOOST_FORCEINLINE iterator insert(const_iterator, value_type const& value) { return table_.insert(value).first; @@ -241,6 +250,15 @@ namespace boost { return table_.insert(std::move(value)).first; } + template + BOOST_FORCEINLINE typename std::enable_if< + detail::transparent_non_iterable::value, + iterator>::type + insert(const_iterator, K&& k) + { + return table_.try_emplace(std::forward(k)).first; + } + template void insert(InputIterator first, InputIterator last) { diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 9542b492..1bd4ba3d 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -390,6 +390,15 @@ namespace boost { return this->emplace(boost::move(x)); } + template + typename boost::enable_if_c< + detail::transparent_non_iterable::value, + std::pair >::type + insert(BOOST_FWD_REF(Key) k) + { + return table_.try_emplace_unique(boost::forward(k)); + } + iterator insert(const_iterator hint, value_type const& x) { return this->emplace_hint(hint, x); @@ -400,6 +409,15 @@ namespace boost { return this->emplace_hint(hint, boost::move(x)); } + template + typename boost::enable_if_c< + detail::transparent_non_iterable::value, + iterator>::type + insert(const_iterator hint, BOOST_FWD_REF(Key) k) + { + return table_.try_emplace_hint_unique(hint, boost::forward(k)); + } + template void insert(InputIt, InputIt); #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) From 3fb516367fdf1d8b6822d655c0184269edfc89f9 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Fri, 16 Dec 2022 15:47:40 -0800 Subject: [PATCH 34/44] Add transparent try_emplace() docs to unordered_map --- doc/unordered/unordered_map.adoc | 83 ++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/doc/unordered/unordered_map.adoc b/doc/unordered/unordered_map.adoc index 40e1ba84..7861148b 100644 --- a/doc/unordered/unordered_map.adoc +++ b/doc/unordered/unordered_map.adoc @@ -113,10 +113,14 @@ namespace boost { std::pair xref:#unordered_map_try_emplace[try_emplace](const key_type& k, Args&&... args); template std::pair xref:#unordered_map_try_emplace[try_emplace](key_type&& k, Args&&... args); + template + std::pair xref:#unordered_map_transparent_try_emplace[try_emplace](K&& k, Args&&... args); template iterator xref:#unordered_map_try_emplace_with_hint[try_emplace](const_iterator hint, const key_type& k, Args&&... args); template iterator xref:#unordered_map_try_emplace_with_hint[try_emplace](const_iterator hint, key_type&& k, Args&&... args); + template + iterator xref:#unordered_map_transparent_try_emplace_with_hint[try_emplace](const_iterator hint, K&& k, Args&&... args); template std::pair xref:#unordered_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj); template @@ -1030,6 +1034,45 @@ Since existing `std::pair` implementations don't support `std::piecewise_constru --- +==== Transparent try_emplace +```c++ +template + std::pair try_emplace(K&& k, Args&&... args) +``` + +Inserts a new node into the container if there is no existing element with key `k` contained within it. + +If there is an existing element with key `k` this function does nothing. + +This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. + +[horizontal] +Returns:;; The bool component of the return type is true if an insert took place. + ++ +If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. +Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. +Notes:;; This function is similiar to xref:#unordered_map_emplace[emplace] except the `value_type` is constructed using: + ++ +-- +```c++ +value_type(std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(args)...)) +``` + +instead of xref:#unordered_map_emplace[emplace] which simply forwards all arguments to ``value_type``'s constructor. + +Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + +Pointers and references to elements are never invalidated. + +If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. + +Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`. +-- + +--- + ==== try_emplace with Hint ```c++ template @@ -1044,6 +1087,46 @@ If there is an existing element with key `k` this function does nothing. `hint` is a suggestion to where the element should be inserted. +[horizontal] +Returns:;; If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. +Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. +Notes:;; This function is similiar to xref:#unordered_map_emplace_hint[emplace_hint] except the `value_type` is constructed using: + ++ +-- +```c++ +value_type(std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(args)...)) +``` + +instead of xref:#unordered_map_emplace_hint[emplace_hint] which simply forwards all arguments to ``value_type``'s constructor. + +The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + +Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + +Pointers and references to elements are never invalidated. + +If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. + +Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`. +-- + +==== Transparent try_emplace with Hint +```c++ +template + iterator try_emplace(const_iterator hint, K&& k, Args&&... args); +``` + +Inserts a new node into the container if there is no existing element with key `k` contained within it. + +If there is an existing element with key `k` this function does nothing. + +`hint` is a suggestion to where the element should be inserted. + +This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. + + [horizontal] Returns:;; If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. From 61aedca94065c2778a583f9bc169a883bdd1ed7d Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 18 Dec 2022 11:08:20 +0100 Subject: [PATCH 35/44] documented P2363 overloads for unordered_map --- doc/unordered/unordered_map.adoc | 175 +++++++++++-------------------- 1 file changed, 63 insertions(+), 112 deletions(-) diff --git a/doc/unordered/unordered_map.adoc b/doc/unordered/unordered_map.adoc index 7861148b..2cc08369 100644 --- a/doc/unordered/unordered_map.adoc +++ b/doc/unordered/unordered_map.adoc @@ -114,32 +114,36 @@ namespace boost { template std::pair xref:#unordered_map_try_emplace[try_emplace](key_type&& k, Args&&... args); template - std::pair xref:#unordered_map_transparent_try_emplace[try_emplace](K&& k, Args&&... args); + std::pair xref:#unordered_map_try_emplace[try_emplace](K&& k, Args&&... args); template iterator xref:#unordered_map_try_emplace_with_hint[try_emplace](const_iterator hint, const key_type& k, Args&&... args); template iterator xref:#unordered_map_try_emplace_with_hint[try_emplace](const_iterator hint, key_type&& k, Args&&... args); template - iterator xref:#unordered_map_transparent_try_emplace_with_hint[try_emplace](const_iterator hint, K&& k, Args&&... args); + iterator xref:#unordered_map_try_emplace_with_hint[try_emplace](const_iterator hint, K&& k, Args&&... args); template std::pair xref:#unordered_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj); template std::pair xref:#unordered_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj); + template + std::pair xref:#unordered_map_insert_or_assign[insert_or_assign](K&& k, M&& obj); template iterator xref:#unordered_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, const key_type& k, M&& obj); template iterator xref:#unordered_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, key_type&& k, M&& obj); + template + iterator xref:#unordered_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, K&& k, M&& obj); node_type xref:#unordered_map_extract_by_iterator[extract](const_iterator position); node_type xref:#unordered_map_extract_by_key[extract](const key_type& k); - template node_type xref:#unordered_map_transparent_extract_by_key[extract](K&& k); + template node_type xref:#unordered_map_extract_by_key[extract](K&& k); insert_return_type xref:#unordered_map_insert_with_node_handle[insert](node_type&& nh); iterator xref:#unordered_map_insert_with_hint_and_node_handle[insert](const_iterator hint, node_type&& nh); iterator xref:#unordered_map_erase_by_position[erase](iterator position); iterator xref:#unordered_map_erase_by_position[erase](const_iterator position); size_type xref:#unordered_map_erase_by_key[erase](const key_type& k); - template size_type xref:#unordered_map_transparent_erase_by_key[erase](K&& k); + template size_type xref:#unordered_map_erase_by_key[erase](K&& k); iterator xref:#unordered_map_erase_range[erase](const_iterator first, const_iterator last); void xref:#unordered_map_quick_erase[quick_erase](const_iterator position); void xref:#unordered_map_erase_return_void[erase_return_void](const_iterator position); @@ -191,14 +195,18 @@ namespace boost { // element access mapped_type& xref:#unordered_map_operator[operator[+]+](const key_type& k); mapped_type& xref:#unordered_map_operator[operator[+]+](key_type&& k); + template mapped_type& xref:#unordered_map_operator[operator[+]+](K&& k); mapped_type& xref:#unordered_map_at[at](const key_type& k); const mapped_type& xref:#unordered_map_at[at](const key_type& k) const; + template mapped_type& xref:#unordered_map_at[at](const K& k); + template const mapped_type& xref:#unordered_map_at[at](const K& k) const; // bucket interface size_type xref:#unordered_map_bucket_count[bucket_count]() const noexcept; size_type xref:#unordered_map_max_bucket_count[max_bucket_count]() const noexcept; size_type xref:#unordered_map_bucket_size[bucket_size](size_type n) const; size_type xref:#unordered_map_bucket[bucket](const key_type& k) const; + template size_type xref:#unordered_map_bucket[bucket](const K& k) const; local_iterator xref:#unordered_map_begin_2[begin](size_type n); const_local_iterator xref:#unordered_map_begin_2[begin](size_type n) const; local_iterator xref:#unordered_map_end_2[end](size_type n); @@ -1001,41 +1009,6 @@ template std::pair try_emplace(const key_type& k, Args&&... args); template std::pair try_emplace(key_type&& k, Args&&... args); -``` - -Inserts a new node into the container if there is no existing element with key `k` contained within it. - -If there is an existing element with key `k` this function does nothing. - -[horizontal] -Returns:;; The bool component of the return type is true if an insert took place. + -+ -If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. -Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. -Notes:;; This function is similiar to xref:#unordered_map_emplace[emplace] except the `value_type` is constructed using: + -+ --- -```c++ -value_type(std::piecewise_construct, - std::forward_as_tuple(boost::forward(k)), - std::forward_as_tuple(boost::forward(args)...)) -``` - -instead of xref:#unordered_map_emplace[emplace] which simply forwards all arguments to ``value_type``'s constructor. - -Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - -Pointers and references to elements are never invalidated. - -If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. - -Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`. --- - ---- - -==== Transparent try_emplace -```c++ template std::pair try_emplace(K&& k, Args&&... args) ``` @@ -1044,8 +1017,6 @@ Inserts a new node into the container if there is no existing element with key ` If there is an existing element with key `k` this function does nothing. -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - [horizontal] Returns:;; The bool component of the return type is true if an insert took place. + + @@ -1055,9 +1026,15 @@ Notes:;; This function is similiar to xref:#unordered_map_emplace[emplace] excep + -- ```c++ +// first two overloads value_type(std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(args)...)) + +// third overload +value_type(std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(args)...)) ``` instead of xref:#unordered_map_emplace[emplace] which simply forwards all arguments to ``value_type``'s constructor. @@ -1066,6 +1043,8 @@ Can invalidate iterators, but only if the insert causes the load factor to be gr Pointers and references to elements are never invalidated. +The `template ` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. + If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`. @@ -1079,41 +1058,6 @@ template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); -``` - -Inserts a new node into the container if there is no existing element with key `k` contained within it. - -If there is an existing element with key `k` this function does nothing. - -`hint` is a suggestion to where the element should be inserted. - -[horizontal] -Returns:;; If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. -Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. -Notes:;; This function is similiar to xref:#unordered_map_emplace_hint[emplace_hint] except the `value_type` is constructed using: + -+ --- -```c++ -value_type(std::piecewise_construct, - std::forward_as_tuple(boost::forward(k)), - std::forward_as_tuple(boost::forward(args)...)) -``` - -instead of xref:#unordered_map_emplace_hint[emplace_hint] which simply forwards all arguments to ``value_type``'s constructor. - -The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. - -Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. - -Pointers and references to elements are never invalidated. - -If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. - -Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`. --- - -==== Transparent try_emplace with Hint -```c++ template iterator try_emplace(const_iterator hint, K&& k, Args&&... args); ``` @@ -1124,9 +1068,6 @@ If there is an existing element with key `k` this function does nothing. `hint` is a suggestion to where the element should be inserted. -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - - [horizontal] Returns:;; If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. @@ -1134,9 +1075,15 @@ Notes:;; This function is similiar to xref:#unordered_map_emplace_hint[emplace_h + -- ```c++ +// first two overloads value_type(std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(args)...)) + +// third overload +value_type(std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(args)...)) ``` instead of xref:#unordered_map_emplace_hint[emplace_hint] which simply forwards all arguments to ``value_type``'s constructor. @@ -1147,6 +1094,8 @@ Can invalidate iterators, but only if the insert causes the load factor to be gr Pointers and references to elements are never invalidated. +The `template ` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. + If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`. @@ -1160,6 +1109,8 @@ template std::pair insert_or_assign(const key_type& k, M&& obj); template std::pair insert_or_assign(key_type&& k, M&& obj); +template + std::pair insert_or_assign(K&& k, M&& obj); ``` Inserts a new element into the container or updates an existing one by assigning to the contained value. @@ -1168,9 +1119,15 @@ If there is an element with key `k`, then it is updated by assigning `boost::for If there is no such element, it is added to the container as: ```c++ +// first two overloads value_type(std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(obj))) + +// third overload +value_type(std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(obj))) ``` [horizontal] @@ -1180,7 +1137,9 @@ If an insert took place, then the iterator points to the newly inserted element. Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + -Pointers and references to elements are never invalidated. +Pointers and references to elements are never invalidated. + ++ +The `template` only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1190,6 +1149,8 @@ template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); +template + iterator insert_or_assign(const_iterator hint, K&& k, M&& obj); ``` Inserts a new element into the container or updates an existing one by assigning to the contained value. @@ -1198,9 +1159,15 @@ If there is an element with key `k`, then it is updated by assigning `boost::for If there is no such element, it is added to the container as: ```c++ +// first two overloads value_type(std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(obj))) + +// third overload +value_type(std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(obj))) ``` `hint` is a suggestion to where the element should be inserted. @@ -1212,7 +1179,9 @@ Notes:;; The standard is fairly vague on the meaning of the hint. But the only p + Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + -Pointers and references to elements are never invalidated. +Pointers and references to elements are never invalidated. + ++ +The `template` only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1232,30 +1201,17 @@ Notes:;; A node extracted using this method can be inserted into a compatible `u ==== Extract by Key ```c++ node_type extract(const key_type& k); -``` - -Removes an element with key equivalent to `k`. - -[horizontal] -Returns:;; A `node_type` owning the element if found, otherwise an empty `node_type`. -Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. -Notes:;; A node extracted using this method can be inserted into a compatible `unordered_multimap`. - ---- - -==== Transparent Extract by Key -```c++ template node_type extract(K&& k); ``` Removes an element with key equivalent to `k`. -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - [horizontal] Returns:;; A `node_type` owning the element if found, otherwise an empty `node_type`. Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. -Notes:;; A node extracted using this method can be inserted into a compatible `unordered_multimap`. +Notes:;; A node extracted using this method can be inserted into a compatible `unordered_multimap`. + ++ +The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1334,28 +1290,15 @@ Notes:;; In older versions this could be inefficient because it had to search th ==== Erase by Key ```c++ size_type erase(const key_type& k); -``` - -Erase all elements with key equivalent to `k`. - -[horizontal] -Returns:;; The number of elements erased. -Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. - ---- - -==== Transparent Erase by Key -```c++ template size_type erase(K&& k); ``` Erase all elements with key equivalent to `k`. -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - [horizontal] Returns:;; The number of elements erased. Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. +Notes:;; The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1573,6 +1516,7 @@ Notes:;; The `template ` overloads only participate in overload reso ```c++ mapped_type& operator[](const key_type& k); mapped_type& operator[](key_type&& k); +template mapped_type& operator[](K&& k); ``` [horizontal] @@ -1581,7 +1525,9 @@ Returns:;; A reference to `x.second` where `x` is the element already in the con Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + + -Pointers and references to elements are never invalidated. +Pointers and references to elements are never invalidated. + ++ +The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1589,11 +1535,14 @@ Pointers and references to elements are never invalidated. ```c++ mapped_type& at(const key_type& k); const mapped_type& at(const key_type& k) const; +template mapped_type& at(const K& k); +template const mapped_type& at(const K& k) const; ``` [horizontal] Returns:;; A reference to `x.second` where `x` is the (unique) element whose key is equivalent to `k`. Throws:;; An exception object of type `std::out_of_range` if no such element is present. +Notes:;; The `template` overloads only participate in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1633,11 +1582,13 @@ Returns:;; The number of elements in bucket `n`. ==== bucket ```c++ size_type bucket(const key_type& k) const; +template size_type bucket(const K& k) const; ``` [horizontal] Returns:;; The index of the bucket which would contain an element with key `k`. Postconditions:;; The return value is less than `bucket_count()`. +Notes:;; The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- From 59d0accce0dbc2f472bbf8182ff44a6ae2a18680 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 18 Dec 2022 11:15:33 +0100 Subject: [PATCH 36/44] documented P2363 overloads for unordered/unordered_multimap --- doc/unordered/unordered_multimap.adoc | 41 ++++++--------------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/doc/unordered/unordered_multimap.adoc b/doc/unordered/unordered_multimap.adoc index 9237d747..bc80967f 100644 --- a/doc/unordered/unordered_multimap.adoc +++ b/doc/unordered/unordered_multimap.adoc @@ -111,14 +111,14 @@ namespace boost { node_type xref:#unordered_multimap_extract_by_iterator[extract](const_iterator position); node_type xref:#unordered_multimap_extract_by_key[extract](const key_type& k); - template node_type xref:#unordered_multimap_transparent_extract_by_key[extract](K&& k); + template node_type xref:#unordered_multimap_extract_by_key[extract](K&& k); iterator xref:#unordered_multimap_insert_with_node_handle[insert](node_type&& nh); iterator xref:#unordered_multimap_insert_with_hint_and_node_handle[insert](const_iterator hint, node_type&& nh); iterator xref:#unordered_multimap_erase_by_position[erase](iterator position); iterator xref:#unordered_multimap_erase_by_position[erase](const_iterator position); size_type xref:#unordered_multimap_erase_by_key[erase](const key_type& k); - template size_type xref:#unordered_multimap_transparent_erase_by_key[erase](K&& k); + template size_type xref:#unordered_multimap_erase_by_key[erase](K&& k); iterator xref:#unordered_multimap_erase_range[erase](const_iterator first, const_iterator last); void xref:#unordered_multimap_quick_erase[quick_erase](const_iterator position); void xref:#unordered_multimap_erase_return_void[erase_return_void](const_iterator position); @@ -172,6 +172,7 @@ namespace boost { size_type xref:#unordered_multimap_max_bucket_count[max_bucket_count]() const noexcept; size_type xref:#unordered_multimap_bucket_size[bucket_size](size_type n) const; size_type xref:#unordered_multimap_bucket[bucket](const key_type& k) const; + template size_type xref:#unordered_multimap_bucket[bucket](const K& k) const; local_iterator xref:#unordered_multimap_begin_2[begin](size_type n); const_local_iterator xref:#unordered_multimap_begin_2[begin](size_type n) const; local_iterator xref:#unordered_multimap_end_2[end](size_type n); @@ -964,30 +965,17 @@ Notes:;; A node extracted using this method can be inserted into a compatible `u ==== Extract by Key ```c++ node_type extract(const key_type& k); -``` - -Removes an element with key equivalent to `k`. - -[horizontal] -Returns:;; A `node_type` owning the element if found, otherwise an empty `node_type`. -Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. -Notes:;; A node extracted using this method can be inserted into a compatible `unordered_map`. - ---- - -==== Transparent Extract by Key -```c++ template node_type extract(K&& k); ``` Removes an element with key equivalent to `k`. -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - [horizontal] Returns:;; A `node_type` owning the element if found, otherwise an empty `node_type`. Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. -Notes:;; A node extracted using this method can be inserted into a compatible `unordered_map`. +Notes:;; A node extracted using this method can be inserted into a compatible `unordered_map`. + ++ +The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1060,28 +1048,15 @@ Notes:;; In older versions this could be inefficient because it had to search th ==== Erase by Key ```c++ size_type erase(const key_type& k); -``` - -Erase all elements with key equivalent to `k`. - -[horizontal] -Returns:;; The number of elements erased. -Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. - ---- - -==== Transparent Erase by Key -```c++ template size_type erase(K&& k); ``` Erase all elements with key equivalent to `k`. -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - [horizontal] Returns:;; The number of elements erased. Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. +Notes:;; The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1330,11 +1305,13 @@ Returns:;; The number of elements in bucket `n`. ==== bucket ```c++ size_type bucket(const key_type& k) const; +templat size_type bucket(const K& k) const; ``` [horizontal] Returns:;; The index of the bucket which would contain an element with key `k`. Postconditions:;; The return value is less than `bucket_count()`. +Notes:;; The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- From 24d844983185189f4070664dd2299cca47a28b8d Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 18 Dec 2022 11:47:28 +0100 Subject: [PATCH 37/44] documented P2363 overloads for unordered_set --- doc/unordered/unordered_set.adoc | 87 ++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/doc/unordered/unordered_set.adoc b/doc/unordered/unordered_set.adoc index f8fdd117..d5b1fb9c 100644 --- a/doc/unordered/unordered_set.adoc +++ b/doc/unordered/unordered_set.adoc @@ -100,21 +100,23 @@ namespace boost { template iterator xref:#unordered_set_emplace_hint[emplace_hint](const_iterator position, Args&&... args); std::pair xref:#unordered_set_copy_insert[insert](const value_type& obj); std::pair xref:#unordered_set_move_insert[insert](value_type&& obj); + template std::pair xref:#unordered_set_transparent_insert[insert](K&& k); iterator xref:#unordered_set_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); iterator xref:#unordered_set_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); + template iterator xref:#unordered_set_transparent_insert_with_hint[insert](const_iterator hint, K&& k); template void xref:#unordered_set_insert_iterator_range[insert](InputIterator first, InputIterator last); void xref:#unordered_set_insert_initializer_list[insert](std::initializer_list); node_type xref:#unordered_set_extract_by_iterator[extract](const_iterator position); node_type xref:#unordered_set_extract_by_value[extract](const key_type& k); - template node_type xref:#unordered_set_transparent_extract_by_value[extract](K&& k); + template node_type xref:#unordered_set_extract_by_value[extract](K&& k); insert_return_type xref:#unordered_set_insert_with_node_handle[insert](node_type&& nh); iterator xref:#unordered_set_insert_with_hint_and_node_handle[insert](const_iterator hint, node_type&& nh); iterator xref:#unordered_set_erase_by_position[erase](iterator position); iterator xref:#unordered_set_erase_by_position[erase](const_iterator position); size_type xref:#unordered_set_erase_by_value[erase](const key_type& k); - template size_type xref:#unordered_set_transparent_erase_by_value[erase](K&& k); + template size_type xref:#unordered_set_erase_by_value[erase](K&& k); iterator xref:#unordered_set_erase_range[erase](const_iterator first, const_iterator last); void xref:#unordered_set_quick_erase[quick_erase](const_iterator position); void xref:#unordered_set_erase_return_void[erase_return_void](const_iterator position); @@ -168,6 +170,7 @@ namespace boost { size_type xref:#unordered_set_max_bucket_count[max_bucket_count]() const noexcept; size_type xref:#unordered_set_bucket_size[bucket_size](size_type n) const; size_type xref:#unordered_set_bucket[bucket](const key_type& k) const; + template size_type xref:#unordered_set_bucket[bucket](const K& k) const; local_iterator xref:#unordered_set_begin_2[begin](size_type n); const_local_iterator xref:#unordered_set_begin_2[begin](size_type n) const; local_iterator xref:#unordered_set_end_2[end](size_type n); @@ -847,6 +850,27 @@ Pointers and references to elements are never invalidated. --- +==== Transparent Insert +```c++ +template std::pair insert(K&& k); +``` + +Inserts an element constructed from `std::forward(k)` in the container if and only if there is no element in the container with an equivalent key. + +[horizontal] +Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] from `k`. +Returns:;; The bool component of the return type is true if an insert took place. + ++ +If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. +Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. +Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + ++ +Pointers and references to elements are never invalidated. + ++ +This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. + +--- + ==== Copy Insert with Hint ```c++ iterator insert(const_iterator hint, const value_type& obj); @@ -888,6 +912,29 @@ Pointers and references to elements are never invalidated. --- +==== Transparent Insert with Hint +```c++ +template iterator insert(const_iterator hint, K&& k); +``` + +Inserts an element constructed from `std::forward(k)` in the container if and only if there is no element in the container with an equivalent key. + +`hint` is a suggestion to where the element should be inserted. + +[horizontal] +Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] from `k`. +Returns:;; If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. +Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. +Notes:;; The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same key. + ++ +Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. + ++ +Pointers and references to elements are never invalidated. + ++ +This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. + +--- + ==== Insert Iterator Range ```c++ template void insert(InputIterator first, InputIterator last); @@ -936,30 +983,17 @@ Notes:;; In C++17 a node extracted using this method can be inserted into a comp ==== Extract by Value ```c++ node_type extract(const key_type& k); -``` - -Removes an element with key equivalent to `k`. - -[horizontal] -Returns:;; A `node_type` owning the element if found, otherwise an empty `node_type`. -Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. -Notes:;; In C++17 a node extracted using this method can be inserted into a compatible `unordered_multiset`, but that is not supported yet. - ---- - -==== Transparent Extract by Value -```c++ template node_type extract(K&& k); ``` Removes an element with key equivalent to `k`. -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - [horizontal] Returns:;; A `node_type` owning the element if found, otherwise an empty `node_type`. Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. -Notes:;; In C++17 a node extracted using this method can be inserted into a compatible `unordered_multiset`, but that is not supported yet. +Notes:;; In C++17 a node extracted using this method can be inserted into a compatible `unordered_multiset`, but that is not supported yet. + ++ +The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1038,28 +1072,15 @@ Notes:;; In older versions this could be inefficient because it had to search th ==== Erase by Value ```c++ size_type erase(const key_type& k); -``` - -Erase all elements with key equivalent to `k`. - -[horizontal] -Returns:;; The number of elements erased. -Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. - ---- - -==== Transparent Erase by Value -```c++ template size_type erase(K&& k); ``` Erase all elements with key equivalent to `k`. -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - [horizontal] Returns:;; The number of elements erased. Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. +Notes:;; The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1309,11 +1330,13 @@ Returns:;; The number of elements in bucket `n`. ==== bucket ```c++ size_type bucket(const key_type& k) const; +template size_type bucket(const K& k) const; ``` [horizontal] Returns:;; The index of the bucket which would contain an element with key `k`. Postconditions:;; The return value is less than `bucket_count()`. +Notes:;; The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- From 0e9523a0a49360020fbefb6ded10d34ec17ae24f Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 18 Dec 2022 11:57:41 +0100 Subject: [PATCH 38/44] documented P2363 overloads for unordered_multiset --- doc/unordered/unordered_multiset.adoc | 43 +++++++-------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/doc/unordered/unordered_multiset.adoc b/doc/unordered/unordered_multiset.adoc index ebaec9a5..2495e570 100644 --- a/doc/unordered/unordered_multiset.adoc +++ b/doc/unordered/unordered_multiset.adoc @@ -107,14 +107,14 @@ namespace boost { node_type xref:#unordered_multiset_extract_by_iterator[extract](const_iterator position); node_type xref:#unordered_multiset_extract_by_value[extract](const key_type& k); - template node_type xref:#unordered_multiset_transparent_extract_by_value[extract](K&& k); + template node_type xref:#unordered_multiset_extract_by_value[extract](K&& k); iterator xref:#unordered_multiset_insert_with_node_handle[insert](node_type&& nh); iterator xref:#unordered_multiset_insert_with_hint_and_node_handle[insert](const_iterator hint, node_type&& nh); iterator xref:#unordered_multiset_erase_by_position[erase](iterator position); iterator xref:#unordered_multiset_erase_by_position[erase](const_iterator position); size_type xref:#unordered_multiset_erase_by_value[erase](const key_type& k); - template size_type xref:#unordered_multiset_transparent_erase_by_value[erase](K&& x); + template size_type xref:#unordered_multiset_erase_by_value[erase](K&& x); iterator xref:#unordered_multiset_erase_range[erase](const_iterator first, const_iterator last); void xref:#unordered_multiset_quick_erase[quick_erase](const_iterator position); void xref:#unordered_multiset_erase_return_void[erase_return_void](const_iterator position); @@ -168,6 +168,7 @@ namespace boost { size_type xref:#unordered_multiset_max_bucket_count[max_bucket_count]() const noexcept; size_type xref:#unordered_multiset_bucket_size[bucket_size](size_type n) const; size_type xref:#unordered_multiset_bucket[bucket](const key_type& k) const; + template size_type xref:#unordered_multiset_bucket[bucket](const K& k) const; local_iterator xref:#unordered_multiset_begin_2[begin](size_type n); const_local_iterator xref:#unordered_multiset_begin_2[begin](size_type n) const; local_iterator xref:#unordered_multiset_end_2[end](size_type n); @@ -922,6 +923,7 @@ Notes:;; A node extracted using this method can be inserted into a compatible `u ==== Extract by Value ```c++ node_type extract(const key_type& k); +template node_type extract(K&& k); ``` Removes an element with key equivalent to `k`. @@ -929,23 +931,9 @@ Removes an element with key equivalent to `k`. [horizontal] Returns:;; A `node_type` owning the element if found, otherwise an empty `node_type`. Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. -Notes:;; A node extracted using this method can be inserted into a compatible `unordered_set`. - ---- - -==== Transparent Extract by Value -```c++ -template node_type extract(K&& k); -``` - -Removes an element with key equivalent to `k`. - -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - -[horizontal] -Returns:;; A `node_type` owning the element if found, otherwise an empty `node_type`. -Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`.[horizontal] -Notes:;; A node extracted using this method can be inserted into a compatible `unordered_set`. +Notes:;; A node extracted using this method can be inserted into a compatible `unordered_set`. + ++ +The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1018,28 +1006,15 @@ Notes:;; In older versions this could be inefficient because it had to search th ==== Erase by Value ```c++ size_type erase(const key_type& k); -``` - -Erase all elements with key equivalent to `k`. - -[horizontal] -Returns:;; The number of elements erased. -Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. - ---- - -==== Transparent Erase by Value -```c++ template size_type erase(K&& x); ``` Erase all elements with key equivalent to `k`. -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - [horizontal] Returns:;; The number of elements erased. Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. +Notes:;; The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1288,11 +1263,13 @@ Returns:;; The number of elements in bucket `n`. ==== bucket ```c++ size_type bucket(const key_type& k) const; +template size_type bucket(const K& k) const; ``` [horizontal] Returns:;; The index of the bucket which would contain an element with key `k`. Postconditions:;; The return value is less than `bucket_count()`. +Notes:;; The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- From c646f3e3ca533bd8d6a8289352cbc295cceb0e57 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 18 Dec 2022 12:16:52 +0100 Subject: [PATCH 39/44] documented P2363 overloads for unordered_flat_map --- doc/unordered/unordered_flat_map.adoc | 80 +++++++++++++++++++++------ 1 file changed, 62 insertions(+), 18 deletions(-) diff --git a/doc/unordered/unordered_flat_map.adoc b/doc/unordered/unordered_flat_map.adoc index 27af35cd..e1b27499 100644 --- a/doc/unordered/unordered_flat_map.adoc +++ b/doc/unordered/unordered_flat_map.adoc @@ -134,23 +134,31 @@ namespace boost { std::pair xref:#unordered_flat_map_try_emplace[try_emplace](const key_type& k, Args&&... args); template std::pair xref:#unordered_flat_map_try_emplace[try_emplace](key_type&& k, Args&&... args); + template + std::pair xref:#unordered_flat_map_try_emplace[try_emplace](K&& k, Args&&... args); template iterator xref:#unordered_flat_map_try_emplace_with_hint[try_emplace](const_iterator hint, const key_type& k, Args&&... args); template iterator xref:#unordered_flat_map_try_emplace_with_hint[try_emplace](const_iterator hint, key_type&& k, Args&&... args); + template + iterator xref:#unordered_flat_map_try_emplace_with_hint[try_emplace](const_iterator hint, K&& k, Args&&... args); template std::pair xref:#unordered_flat_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj); template std::pair xref:#unordered_flat_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj); + template + std::pair xref:#unordered_flat_map_insert_or_assign[insert_or_assign](K&& k, M&& obj); template iterator xref:#unordered_flat_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, const key_type& k, M&& obj); template iterator xref:#unordered_flat_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, key_type&& k, M&& obj); + template + iterator xref:#unordered_flat_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, K&& k, M&& obj); void xref:#unordered_flat_map_erase_by_position[erase](iterator position); void xref:#unordered_flat_map_erase_by_position[erase](const_iterator position); size_type xref:#unordered_flat_map_erase_by_key[erase](const key_type& k); - template size_type xref:#unordered_flat_map_transparent_erase_by_key[erase](K&& k); + template size_type xref:#unordered_flat_map_erase_by_key[erase](K&& k); iterator xref:#unordered_flat_map_erase_range[erase](const_iterator first, const_iterator last); void xref:#unordered_flat_map_swap[swap](unordered_flat_map& other) noexcept(boost::allocator_traits::is_always_equal::value || @@ -189,8 +197,11 @@ namespace boost { // element access mapped_type& xref:#unordered_flat_map_operator[operator[+]+](const key_type& k); mapped_type& xref:#unordered_flat_map_operator[operator[+]+](key_type&& k); + template mapped_type& xref:#unordered_flat_map_operator[operator[+]+](K&& k); mapped_type& xref:#unordered_flat_map_at[at](const key_type& k); const mapped_type& xref:#unordered_flat_map_at[at](const key_type& k) const; + template mapped_type& xref:#unordered_flat_map_at[at](const K& k); + template const mapped_type& xref:#unordered_flat_map_at[at](const K& k) const; // bucket interface size_type xref:#unordered_flat_map_bucket_count[bucket_count]() const noexcept; @@ -860,6 +871,8 @@ template std::pair try_emplace(const key_type& k, Args&&... args); template std::pair try_emplace(key_type&& k, Args&&... args); +template + std::pair try_emplace(K&& k, Args&&... args); ``` Inserts a new node into the container if there is no existing element with key `k` contained within it. @@ -876,15 +889,23 @@ if there is an element with an equivalent key; otherwise, the construction is of + -- ```c++ +// first two overloads value_type(std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(args)...)) + +// third overload +value_type(std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(args)...)) ``` unlike xref:#unordered_flat_map_emplace[emplace], which simply forwards all arguments to ``value_type``'s constructor. Can invalidate iterators pointers and references, but only if the insert causes the load to be greater than the maximum load. +The `template ` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. + -- --- @@ -895,6 +916,8 @@ template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); +template + iterator try_emplace(const_iterator hint, K&& k, Args&&... args); ``` Inserts a new node into the container if there is no existing element with key `k` contained within it. @@ -911,15 +934,23 @@ if there is an element with an equivalent key; otherwise, the construction is of + -- ```c++ +// first two overloads value_type(std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(args)...)) + +// third overload +value_type(std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(args)...)) ``` unlike xref:#unordered_flat_map_emplace_hint[emplace_hint], which simply forwards all arguments to ``value_type``'s constructor. Can invalidate iterators pointers and references, but only if the insert causes the load to be greater than the maximum load. +The `template ` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. + -- --- @@ -930,6 +961,8 @@ template std::pair insert_or_assign(const key_type& k, M&& obj); template std::pair insert_or_assign(key_type&& k, M&& obj); +template + std::pair insert_or_assign(K&& k, M&& obj); ``` Inserts a new element into the container or updates an existing one by assigning to the contained value. @@ -938,9 +971,15 @@ If there is an element with key `k`, then it is updated by assigning `boost::for If there is no such element, it is added to the container as: ```c++ +// first two overloads value_type(std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(obj))) + +// third overload +value_type(std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(obj))) ``` [horizontal] @@ -948,7 +987,9 @@ Returns:;; The `bool` component of the return type is `true` if an insert took p + If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. -Notes:;; Can invalidate iterators pointers and references, but only if the insert causes the load to be greater than the maximum load. + +Notes:;; Can invalidate iterators pointers and references, but only if the insert causes the load to be greater than the maximum load. + ++ +The `template` only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -958,6 +999,8 @@ template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); +template + iterator insert_or_assign(const_iterator hint, K&& k, M&& obj); ``` Inserts a new element into the container or updates an existing one by assigning to the contained value. @@ -966,9 +1009,15 @@ If there is an element with key `k`, then it is updated by assigning `boost::for If there is no such element, it is added to the container as: ```c++ +// first two overloads value_type(std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(obj))) + +// third overload +value_type(std::piecewise_construct, + std::forward_as_tuple(boost::forward(k)), + std::forward_as_tuple(boost::forward(obj))) ``` `hint` is a suggestion to where the element should be inserted. This implementation ignores it. @@ -976,7 +1025,9 @@ value_type(std::piecewise_construct, [horizontal] Returns:;; If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. -Notes:;; Can invalidate iterators, pointers and references, but only if the insert causes the load to be greater than the maximum load. +Notes:;; Can invalidate iterators, pointers and references, but only if the insert causes the load to be greater than the maximum load. + ++ +The `template` only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -998,28 +1049,15 @@ Throws:;; Nothing. ==== Erase by Key ```c++ size_type erase(const key_type& k); -``` - -Erase all elements with key equivalent to `k`. - -[horizontal] -Returns:;; The number of elements erased. -Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. - ---- - -==== Transparent Erase by Key -```c++ template size_type erase(K&& k); ``` Erase all elements with key equivalent to `k`. -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - [horizontal] Returns:;; The number of elements erased. Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. +Notes:;; The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1172,13 +1210,16 @@ Notes:;; The `template ` overloads only participate in overload reso ```c++ mapped_type& operator[](const key_type& k); mapped_type& operator[](key_type&& k); +template mapped_type& operator[](K&& k); ``` [horizontal] Effects:;; If the container does not already contain an element with a key equivalent to `k`, inserts the value `std::pair(k, mapped_type())`. Returns:;; A reference to `x.second` where `x` is the element already in the container, or the newly inserted element with a key equivalent to `k`. Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. -Notes:;; Can invalidate iterators, pointers and references, but only if the insert causes the load to be greater than the maximum load. +Notes:;; Can invalidate iterators, pointers and references, but only if the insert causes the load to be greater than the maximum load. + ++ +The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- @@ -1186,11 +1227,14 @@ Notes:;; Can invalidate iterators, pointers and references, but only if the inse ```c++ mapped_type& at(const key_type& k); const mapped_type& at(const key_type& k) const; +template mapped_type& at(const K& k); +template const mapped_type& at(const K& k) const; ``` [horizontal] Returns:;; A reference to `x.second` where `x` is the (unique) element whose key is equivalent to `k`. Throws:;; An exception object of type `std::out_of_range` if no such element is present. +Notes:;; The `template` overloads only participate in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- From 0391b6dafc90d664f174c9e2cdfb16ffebed3bc7 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 18 Dec 2022 12:29:44 +0100 Subject: [PATCH 40/44] documented P2363 overloads for unordered_flat_set --- doc/unordered/unordered_flat_set.adoc | 63 +++++++++++++++++++-------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/doc/unordered/unordered_flat_set.adoc b/doc/unordered/unordered_flat_set.adoc index cd22733a..c0902792 100644 --- a/doc/unordered/unordered_flat_set.adoc +++ b/doc/unordered/unordered_flat_set.adoc @@ -116,15 +116,17 @@ namespace boost { template iterator xref:#unordered_flat_set_emplace_hint[emplace_hint](const_iterator position, Args&&... args); std::pair xref:#unordered_flat_set_copy_insert[insert](const value_type& obj); std::pair xref:#unordered_flat_set_move_insert[insert](value_type&& obj); - iterator xref:#unordered_flat_set_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); - iterator xref:#unordered_flat_set_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); + template std::pair xref:#unordered_flat_set_transparent_insert[insert](K&& k); + iterator xref:#unordered_flat_set_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); + iterator xref:#unordered_flat_set_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); + template iterator xref:#unordered_flat_set_transparent_insert_with_hint[insert](const_iterator hint, value_type&& obj); template void xref:#unordered_flat_set_insert_iterator_range[insert](InputIterator first, InputIterator last); void xref:#unordered_flat_set_insert_initializer_list[insert](std::initializer_list); void xref:#unordered_flat_set_erase_by_position[erase](iterator position); void xref:#unordered_flat_set_erase_by_position[erase](const_iterator position); size_type xref:#unordered_flat_set_erase_by_key[erase](const key_type& k); - template size_type xref:#unordered_flat_set_transparent_erase_by_key[erase](K&& k); + template size_type xref:#unordered_flat_set_erase_by_key[erase](K&& k); iterator xref:#unordered_flat_set_erase_range[erase](const_iterator first, const_iterator last); void xref:#unordered_flat_set_swap[swap](unordered_flat_set& other) noexcept(boost::allocator_traits::is_always_equal::value || @@ -736,6 +738,25 @@ Notes:;; Can invalidate iterators, pointers and references, but only if the inse --- +==== Transparent Insert +```c++ +template std::pair insert(K&& k); +``` + +Inserts an element constructed from `std::forward(k)` in the container if and only if there is no element in the container with an equivalent key. + +[horizontal] +Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] from `k`. +Returns:;; The bool component of the return type is true if an insert took place. + ++ +If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. +Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. +Notes:;; Can invalidate iterators, pointers and references, but only if the insert causes the load to be greater than the maximum load. + ++ +This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. + +--- + ==== Copy Insert with Hint ```c++ iterator insert(const_iterator hint, const value_type& obj); @@ -773,6 +794,27 @@ Notes:;; Can invalidate iterators, pointers and references, but only if the inse --- +==== Transparent Insert with Hint +```c++ +template std::pair insert(const_iterator hint, K&& k); +``` + +Inserts an element constructed from `std::forward(k)` in the container if and only if there is no element in the container with an equivalent key. + +`hint` is a suggestion to where the element should be inserted. This implementation ignores it. + +[horizontal] +Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] from `k`. +Returns:;; The bool component of the return type is true if an insert took place. + ++ +If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key. +Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. +Notes:;; Can invalidate iterators, pointers and references, but only if the insert causes the load to be greater than the maximum load. + ++ +This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. + +--- + ==== Insert Iterator Range ```c++ template void insert(InputIterator first, InputIterator last); @@ -818,28 +860,15 @@ Throws:;; Nothing. ==== Erase by Key ```c++ size_type erase(const key_type& k); -``` - -Erase all elements with key equivalent to `k`. - -[horizontal] -Returns:;; The number of elements erased. -Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. - ---- - -==== Transparent Erase by Key -```c++ template size_type erase(K&& k); ``` Erase all elements with key equivalent to `k`. -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. - [horizontal] Returns:;; The number of elements erased. Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`. +Notes:;; The `template` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- From 644295db3aad8a5e6cdc88d16f537a6a807fa34a Mon Sep 17 00:00:00 2001 From: joaquintides Date: Sun, 18 Dec 2022 12:32:30 +0100 Subject: [PATCH 41/44] added release notes for feature/p2363 --- doc/unordered/changes.adoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/unordered/changes.adoc b/doc/unordered/changes.adoc index 3c3f15f6..5a038239 100644 --- a/doc/unordered/changes.adoc +++ b/doc/unordered/changes.adoc @@ -6,6 +6,11 @@ :github-pr-url: https://github.com/boostorg/unordered/pull :cpp: C++ +== Release 1.82.0 + +* Extended heterogeneous lookup to more member functions as specified in + https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2363r3.html[P2363]. + == Release 1.81.0 - Major update * Added fast containers `boost::unordered_flat_map` and `boost::unordered_flat_set` From 1955e4f36ac95b660a8ebe7eb21194dd127f1fc1 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Tue, 20 Dec 2022 11:35:15 +0100 Subject: [PATCH 42/44] fixed constraint notes in transparent insert with hint --- doc/unordered/unordered_flat_set.adoc | 4 ++-- doc/unordered/unordered_set.adoc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/unordered/unordered_flat_set.adoc b/doc/unordered/unordered_flat_set.adoc index c0902792..7d3d69f8 100644 --- a/doc/unordered/unordered_flat_set.adoc +++ b/doc/unordered/unordered_flat_set.adoc @@ -119,7 +119,7 @@ namespace boost { template std::pair xref:#unordered_flat_set_transparent_insert[insert](K&& k); iterator xref:#unordered_flat_set_copy_insert_with_hint[insert](const_iterator hint, const value_type& obj); iterator xref:#unordered_flat_set_move_insert_with_hint[insert](const_iterator hint, value_type&& obj); - template iterator xref:#unordered_flat_set_transparent_insert_with_hint[insert](const_iterator hint, value_type&& obj); + template iterator xref:#unordered_flat_set_transparent_insert_with_hint[insert](const_iterator hint, K&& k); template void xref:#unordered_flat_set_insert_iterator_range[insert](InputIterator first, InputIterator last); void xref:#unordered_flat_set_insert_initializer_list[insert](std::initializer_list); @@ -811,7 +811,7 @@ If an insert took place, then the iterator points to the newly inserted element. Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect. Notes:;; Can invalidate iterators, pointers and references, but only if the insert causes the load to be greater than the maximum load. + + -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. +This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- diff --git a/doc/unordered/unordered_set.adoc b/doc/unordered/unordered_set.adoc index d5b1fb9c..8530b4d4 100644 --- a/doc/unordered/unordered_set.adoc +++ b/doc/unordered/unordered_set.adoc @@ -931,7 +931,7 @@ Can invalidate iterators, but only if the insert causes the load factor to be gr + Pointers and references to elements are never invalidated. + + -This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. +This overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type. --- From 955f94350be3f16e9801946e5d06054086ad6e1e Mon Sep 17 00:00:00 2001 From: joaquintides Date: Tue, 20 Dec 2022 16:46:08 +0100 Subject: [PATCH 43/44] typo --- doc/unordered/unordered_multimap.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/unordered/unordered_multimap.adoc b/doc/unordered/unordered_multimap.adoc index bc80967f..ab5ccbbb 100644 --- a/doc/unordered/unordered_multimap.adoc +++ b/doc/unordered/unordered_multimap.adoc @@ -1305,7 +1305,7 @@ Returns:;; The number of elements in bucket `n`. ==== bucket ```c++ size_type bucket(const key_type& k) const; -templat size_type bucket(const K& k) const; +template size_type bucket(const K& k) const; ``` [horizontal] From 09a32753575363dcc46e610f9e8514b4370911d0 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 12 Jan 2023 01:35:35 +0200 Subject: [PATCH 44/44] Add /doc/pdf/ to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2eec3746..a55d9e5e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /doc/html/ +/doc/pdf/