diff --git a/.gitignore b/.gitignore index 2eec3746..a55d9e5e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /doc/html/ +/doc/pdf/ 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` diff --git a/doc/unordered/unordered_flat_map.adoc b/doc/unordered/unordered_flat_map.adoc index a7f4c7fe..e1b27499 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; @@ -135,28 +134,35 @@ 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 && - 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 @@ -191,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; @@ -606,11 +615,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`. @@ -863,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. @@ -879,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. + -- --- @@ -898,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. @@ -914,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. + -- --- @@ -933,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. @@ -941,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] @@ -951,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. --- @@ -961,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. @@ -969,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. @@ -979,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. --- @@ -1001,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. --- @@ -1043,9 +1078,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. @@ -1176,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. --- @@ -1190,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. --- diff --git a/doc/unordered/unordered_flat_set.adoc b/doc/unordered/unordered_flat_set.adoc index 33fe5dfa..7d3d69f8 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; @@ -117,20 +116,21 @@ 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, 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); 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 && - 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 +565,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`. @@ -739,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); @@ -776,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 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. + +--- + ==== Insert Iterator Range ```c++ template void insert(InputIterator first, InputIterator last); @@ -821,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. --- @@ -863,9 +889,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. diff --git a/doc/unordered/unordered_map.adoc b/doc/unordered/unordered_map.adoc index 40e1ba84..2cc08369 100644 --- a/doc/unordered/unordered_map.adoc +++ b/doc/unordered/unordered_map.adoc @@ -113,29 +113,37 @@ 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_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_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); @@ -187,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); @@ -997,6 +1009,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. @@ -1012,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. @@ -1023,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`. @@ -1036,6 +1058,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. @@ -1051,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. @@ -1064,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`. @@ -1077,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. @@ -1085,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] @@ -1097,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. --- @@ -1107,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. @@ -1115,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. @@ -1129,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. --- @@ -1149,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. --- @@ -1251,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. --- @@ -1490,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] @@ -1498,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. --- @@ -1506,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. --- @@ -1550,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. --- diff --git a/doc/unordered/unordered_multimap.adoc b/doc/unordered/unordered_multimap.adoc index 9237d747..ab5ccbbb 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; +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. --- 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. --- diff --git a/doc/unordered/unordered_set.adoc b/doc/unordered/unordered_set.adoc index f8fdd117..8530b4d4 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 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. + +--- + ==== 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. --- diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 289ea457..c1f12f38 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -21,7 +21,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -31,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +71,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{ @@ -262,20 +271,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) @@ -526,11 +527,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); }; @@ -545,11 +542,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; } @@ -584,11 +577,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) @@ -1032,6 +1021,49 @@ inline void prefetch(const void* p) #endif } +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: * @@ -1190,9 +1222,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_): @@ -1227,22 +1257,35 @@ 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()); }); /* 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; } @@ -1254,19 +1297,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_); @@ -1329,12 +1385,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 @@ -1383,16 +1437,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()); }, @@ -1400,6 +1453,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); @@ -1523,6 +1579,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); @@ -1535,6 +1622,82 @@ 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, +#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 + &&( + is_std_allocator::value|| + !alloc_has_construct::value) + >{} + ); + } + + 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( + 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 */) + { + 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 @@ -1571,12 +1734,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 @@ -1737,17 +1899,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); }); @@ -1877,13 +2035,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;} @@ -1892,7 +2072,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; } } @@ -1922,6 +2102,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 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/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); } 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 diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index ce09af90..16d4f3bd 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -278,24 +278,37 @@ 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 = - table_.try_emplace(std::move(key), std::forward(obj)); - if (iter_bool_pair.second) { - return iter_bool_pair; + auto ibp = table_.try_emplace(std::move(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 + 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 @@ -311,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) { @@ -337,6 +360,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 +386,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) { @@ -427,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; @@ -437,6 +511,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_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_map.hpp b/include/boost/unordered/unordered_map.hpp index 9d80d743..6d05471c 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 \ @@ -706,6 +801,15 @@ namespace boost { boost::move(k), boost::forward(obj)); } + template + typename boost::enable_if_c::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) @@ -722,6 +826,18 @@ namespace boost { .first; } + template + typename boost::enable_if_c::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&); @@ -856,9 +972,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 @@ -878,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); @@ -1584,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)); @@ -2098,6 +2244,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) @@ -2130,6 +2285,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 diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index a054ed64..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) @@ -571,6 +589,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 +1238,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/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/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. 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. 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() 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 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 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) 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; 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(); +} 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))) diff --git a/test/unordered/transparent_tests.cpp b/test/unordered/transparent_tests.cpp index 9852fa84..8d1f781b 100644 --- a/test/unordered/transparent_tests.cpp +++ b/test/unordered/transparent_tests.cpp @@ -1478,6 +1478,312 @@ 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)); +} + +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)); +} + +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); +} + +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); +} + +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() { @@ -1626,6 +1932,134 @@ 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 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 @@ -1646,6 +2080,11 @@ void test_unordered_map() test_map_transparent_equal_range(); test_map_transparent_erase(); test_map_transparent_extract(); + test_map_transparent_try_emplace(); + test_map_transparent_insert_or_assign(); + test_map_transparent_subscript(); + test_map_transparent_at(); + test_map_transparent_bucket(); } { @@ -1658,6 +2097,11 @@ 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(); + test_map_non_transparent_insert_or_assign(); + test_map_non_transparent_subscript(); + test_map_non_transparent_at(); + test_map_non_transparent_bucket(); } { @@ -1671,6 +2115,11 @@ 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(); + test_map_non_transparent_insert_or_assign(); + test_map_non_transparent_subscript(); + test_map_non_transparent_at(); + test_map_non_transparent_bucket(); } { @@ -1684,6 +2133,11 @@ 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(); + test_map_non_transparent_insert_or_assign(); + test_map_non_transparent_subscript(); + test_map_non_transparent_at(); + test_map_non_transparent_bucket(); } } @@ -1700,6 +2154,7 @@ void test_unordered_multimap() test_map_transparent_equal_range(); test_map_transparent_erase(); test_map_transparent_extract(); + test_map_transparent_bucket(); } { @@ -1713,6 +2168,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(); } { @@ -1726,6 +2182,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(); } { @@ -1739,6 +2196,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 @@ -1763,6 +2221,8 @@ void test_unordered_set() test_set_transparent_erase(); test_set_transparent_equal_range(); test_set_transparent_extract(); + test_set_transparent_bucket(); + test_set_transparent_insert(); } { @@ -1775,6 +2235,8 @@ 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(); + test_set_non_transparent_insert(); } { @@ -1787,6 +2249,8 @@ 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(); + test_set_non_transparent_insert(); } { @@ -1799,6 +2263,8 @@ 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(); + test_set_non_transparent_insert(); } } @@ -1815,6 +2281,7 @@ void test_unordered_multiset() test_set_transparent_erase(); test_set_transparent_equal_range(); test_set_transparent_extract(); + test_set_transparent_bucket(); } { @@ -1827,6 +2294,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(); } { @@ -1840,6 +2308,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(); } { @@ -1853,6 +2322,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