diff --git a/bench/bench_set.hpp b/bench/bench_set.hpp index 4af500c..46ca9dd 100644 --- a/bench/bench_set.hpp +++ b/bench/bench_set.hpp @@ -471,7 +471,7 @@ void launch_tests(const char *BoostContName, const char *StdContName) extensions_time< BoostClass >(get_range_t::sorted_unique()); } - }catch(std::exception e){ + }catch(std::exception &e){ std::cout << e.what(); } } diff --git a/bench/bench_static_vector.cpp b/bench/bench_static_vector.cpp index 4553dc7..d9dd8a7 100644 --- a/bench/bench_static_vector.cpp +++ b/bench/bench_static_vector.cpp @@ -137,7 +137,7 @@ int main() std::cout << "varray/std::vector total time comparison:"; compare_times(time_varray,time_standard_vector); - }catch(std::exception e){ + }catch(std::exception &e){ std::cout << e.what(); } return 0; diff --git a/doc/container.qbk b/doc/container.qbk index 4f8ea7f..93ab8a1 100644 --- a/doc/container.qbk +++ b/doc/container.qbk @@ -1240,6 +1240,16 @@ use [*Boost.Container]? There are several reasons for that: [section:release_notes Release Notes] +[section:release_notes_boost_1_69_00 Boost 1.69 Release] + +* Implemented C++20 `contains()` for associative containers as specified in + [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0458r2.html + P0458R2: Checking for Existence of an Element in Associative Containers]. + +* Fixed serious bug in heterogeneous lookup functions (is_transparent was broken). + +[endsect] + [section:release_notes_boost_1_68_00 Boost 1.68 Release] * Improved correctness of [classref boost::container::adaptive_pool adaptive_pool] and many parameters are now compile-time diff --git a/include/boost/container/detail/flat_tree.hpp b/include/boost/container/detail/flat_tree.hpp index e9cbe38..061b840 100644 --- a/include/boost/container/detail/flat_tree.hpp +++ b/include/boost/container/detail/flat_tree.hpp @@ -1201,6 +1201,15 @@ class flat_tree return n; } + BOOST_CONTAINER_FORCEINLINE bool contains(const key_type& x) const + { return this->find(x) != this->cend(); } + + template + BOOST_CONTAINER_FORCEINLINE + typename dtl::enable_if_transparent::type + contains(const K& x) const + { return this->find(x) != this->cend(); } + template BOOST_CONTAINER_FORCEINLINE void merge_unique(flat_tree& source) { diff --git a/include/boost/container/detail/mpl.hpp b/include/boost/container/detail/mpl.hpp index 385f7db..5249dbf 100644 --- a/include/boost/container/detail/mpl.hpp +++ b/include/boost/container/detail/mpl.hpp @@ -76,19 +76,28 @@ struct select1st { return const_cast(x.first); } }; + +template +struct void_t { typedef void type; }; + template -struct is_transparent +struct is_transparent_base { static const bool value = false; }; template -struct is_transparent +struct is_transparent_base::type> { static const bool value = true; }; -template +template +struct is_transparent + : is_transparent_base +{}; + +template struct enable_if_transparent : boost::move_detail::enable_if_c::value, R> {}; diff --git a/include/boost/container/detail/tree.hpp b/include/boost/container/detail/tree.hpp index c32e992..7d02553 100644 --- a/include/boost/container/detail/tree.hpp +++ b/include/boost/container/detail/tree.hpp @@ -1297,6 +1297,15 @@ class tree count(const K& k) const { return size_type(this->icont().count(k, KeyNodeCompare(key_comp()))); } + BOOST_CONTAINER_FORCEINLINE bool contains(const key_type& x) const + { return this->find(x) != this->cend(); } + + template + BOOST_CONTAINER_FORCEINLINE + typename dtl::enable_if_transparent::type + contains(const K& x) const + { return this->find(x) != this->cend(); } + BOOST_CONTAINER_FORCEINLINE iterator lower_bound(const key_type& k) { return iterator(this->icont().lower_bound(k, KeyNodeCompare(key_comp()))); } diff --git a/include/boost/container/flat_map.hpp b/include/boost/container/flat_map.hpp index 9679946..191f3b9 100644 --- a/include/boost/container/flat_map.hpp +++ b/include/boost/container/flat_map.hpp @@ -1340,6 +1340,24 @@ class flat_map BOOST_CONTAINER_FORCEINLINE size_type count(const K& x) const { return static_cast(m_flat_tree.find(x) != m_flat_tree.end()); } + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + bool contains(const key_type& x) const + { return m_flat_tree.find(x) != m_flat_tree.end(); } + + //! Requires: This overload is available only if + //! key_compare::is_transparent exists. + //! + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + template + bool contains(const K& x) const + { return m_flat_tree.find(x) != m_flat_tree.end(); } + //! Returns: An iterator pointing to the first element with key not less //! than k, or a.end() if such an element is not found. //! @@ -2631,6 +2649,24 @@ class flat_multimap BOOST_CONTAINER_FORCEINLINE size_type count(const K& x) const { return m_flat_tree.count(x); } + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + bool contains(const key_type& x) const + { return m_flat_tree.find(x) != m_flat_tree.end(); } + + //! Requires: This overload is available only if + //! key_compare::is_transparent exists. + //! + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + template + bool contains(const K& x) const + { return m_flat_tree.find(x) != m_flat_tree.end(); } + //! Returns: An iterator pointing to the first element with key not less //! than k, or a.end() if such an element is not found. //! diff --git a/include/boost/container/flat_set.hpp b/include/boost/container/flat_set.hpp index cfea7c8..c4dd827 100644 --- a/include/boost/container/flat_set.hpp +++ b/include/boost/container/flat_set.hpp @@ -912,6 +912,23 @@ class flat_set { return static_cast(this->tree_t::find(x) != this->tree_t::cend()); } #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + bool contains(const key_type& x) const; + + //! Requires: This overload is available only if + //! key_compare::is_transparent exists. + //! + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + template + bool contains(const K& x) const; + //! Returns: An iterator pointing to the first element with key not less //! than k, or a.end() if such an element is not found. //! @@ -1685,6 +1702,13 @@ class flat_multiset //! @copydoc ::boost::container::flat_set::count(const key_type& ) const size_type count(const key_type& x) const; + //! @copydoc ::boost::container::flat_set::contains(const key_type& ) const + bool contains(const key_type& x) const; + + //! @copydoc ::boost::container::flat_set::contains(const K& ) const + template + bool contains(const K& x) const; + //! @copydoc ::boost::container::flat_set::lower_bound(const key_type& ) iterator lower_bound(const key_type& x); diff --git a/include/boost/container/map.hpp b/include/boost/container/map.hpp index a88b6a5..02bd74f 100644 --- a/include/boost/container/map.hpp +++ b/include/boost/container/map.hpp @@ -1119,6 +1119,22 @@ class map #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + bool contains(const key_type& x) const; + + //! Requires: This overload is available only if + //! key_compare::is_transparent exists. + //! + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + template + bool contains(const K& x) const; + //! Returns: An iterator pointing to the first element with key not less //! than k, or a.end() if such an element is not found. //! @@ -2017,6 +2033,22 @@ class multimap template size_type count(const K& x) const; + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + bool contains(const key_type& x) const; + + //! Requires: This overload is available only if + //! key_compare::is_transparent exists. + //! + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + template + bool contains(const K& x) const; + //! Returns: An iterator pointing to the first element with key not less //! than k, or a.end() if such an element is not found. //! diff --git a/include/boost/container/set.hpp b/include/boost/container/set.hpp index e300898..68425be 100644 --- a/include/boost/container/set.hpp +++ b/include/boost/container/set.hpp @@ -772,14 +772,24 @@ class set BOOST_CONTAINER_FORCEINLINE size_type count(const K& x) const { return static_cast(this->find(x) != this->cend()); } - //! Returns: The number of elements with key equivalent to x. - //! - //! Complexity: log(size())+count(k) - BOOST_CONTAINER_FORCEINLINE size_type count(const key_type& x) - { return static_cast(this->base_t::find(x) != this->base_t::end()); } - #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + bool contains(const key_type& x) const; + + //! Requires: This overload is available only if + //! key_compare::is_transparent exists. + //! + //! Returns: Returns true if there is an element with key + //! equivalent to key in the container, otherwise false. + //! + //! Complexity: log(size()). + template + bool contains(const K& x) const; + //! Returns: An iterator pointing to the first element with key not less //! than k, or a.end() if such an element is not found. //! @@ -1461,6 +1471,13 @@ class multiset template size_type count(const K& x) const; + //! @copydoc ::boost::container::set::contains(const key_type& ) const + bool contains(const key_type& x) const; + + //! @copydoc ::boost::container::set::contains(const K& ) const + template + bool contains(const K& x) const; + //! @copydoc ::boost::container::set::lower_bound(const key_type& ) iterator lower_bound(const key_type& x); diff --git a/test/flat_map_test.cpp b/test/flat_map_test.cpp index 7a9a3e7..8118591 100644 --- a/test/flat_map_test.cpp +++ b/test/flat_map_test.cpp @@ -454,6 +454,8 @@ struct get_real_stored_allocator > bool test_heterogeneous_lookups() { + BOOST_STATIC_ASSERT((dtl::is_transparent::value)); + BOOST_STATIC_ASSERT(!(dtl::is_transparent >::value)); typedef flat_map map_t; typedef flat_multimap mmap_t; typedef map_t::value_type value_type; @@ -498,6 +500,16 @@ bool test_heterogeneous_lookups() if(cmmap1.count(find_me) != 2) return false; + //contains + if(!map1.contains(find_me)) + return false; + if(!cmap1.contains(find_me)) + return false; + if(!mmap1.contains(find_me)) + return false; + if(!cmmap1.contains(find_me)) + return false; + //lower_bound if(map1.lower_bound(find_me)->second != 'd') return false; @@ -709,4 +721,3 @@ int main() } #include - diff --git a/test/flat_set_test.cpp b/test/flat_set_test.cpp index fc8233a..e720932 100644 --- a/test/flat_set_test.cpp +++ b/test/flat_set_test.cpp @@ -495,6 +495,16 @@ bool test_heterogeneous_lookups() if(cmset1.count(find_me) != 2) return false; + //contains + if(!set1.contains(find_me)) + return false; + if(!cset1.contains(find_me)) + return false; + if(!mset1.contains(find_me)) + return false; + if(!cmset1.contains(find_me)) + return false; + //lower_bound if(*set1.lower_bound(find_me) != 2) return false; diff --git a/test/map_test.cpp b/test/map_test.cpp index 619646a..ec4c33e 100644 --- a/test/map_test.cpp +++ b/test/map_test.cpp @@ -366,6 +366,16 @@ bool test_heterogeneous_lookups() if(cmmap1.count(find_me) != 2) return false; + //contains + if(!map1.contains(find_me)) + return false; + if(!cmap1.contains(find_me)) + return false; + if(!mmap1.contains(find_me)) + return false; + if(!cmmap1.contains(find_me)) + return false; + //lower_bound if(map1.lower_bound(find_me)->second != 'd') return false; diff --git a/test/map_test.hpp b/test/map_test.hpp index 93dbf2f..2ac3efd 100644 --- a/test/map_test.hpp +++ b/test/map_test.hpp @@ -628,16 +628,24 @@ int map_test() } } - //Compare count with std containers + //Compare count/contains with std containers for(int i = 0; i < MaxElem; ++i){ IntType k(i); if(boostmap.count(k) != stdmap.count(i)){ return -1; } + if(boostmap.contains(k) != (stdmap.find(i) != stdmap.end())){ + return -1; + } + if(boostmultimap.count(k) != stdmultimap.count(i)){ return -1; } + + if(boostmultimap.contains(k) != (stdmultimap.find(i) != stdmultimap.end())){ + return -1; + } } //Now do count exercise diff --git a/test/set_test.cpp b/test/set_test.cpp index 6fc311d..7a693fc 100644 --- a/test/set_test.cpp +++ b/test/set_test.cpp @@ -363,6 +363,16 @@ bool test_heterogeneous_lookups() if(cmset1.count(find_me) != 2) return false; + //contains + if(!set1.contains(find_me)) + return false; + if(!cset1.contains(find_me)) + return false; + if(!mset1.contains(find_me)) + return false; + if(!cmset1.contains(find_me)) + return false; + //lower_bound if(*set1.lower_bound(find_me) != 2) return false; diff --git a/test/set_test.hpp b/test/set_test.hpp index b8ca649..c31882a 100644 --- a/test/set_test.hpp +++ b/test/set_test.hpp @@ -580,11 +580,20 @@ int set_test () //Compare count with std containers for(int i = 0; i < MaxElem; ++i){ - IntType count_me(i); - if(boostset.count(count_me) != stdset.count(i)){ + IntType k(i); + if(boostset.count(k) != stdset.count(i)){ return -1; } - if(boostmultiset.count(count_me) != stdmultiset.count(i)){ + + if(boostset.contains(k) != (stdset.find(i) != stdset.end())){ + return -1; + } + + if(boostmultiset.count(k) != stdmultiset.count(i)){ + return -1; + } + + if(boostmultiset.contains(k) != (stdmultiset.find(i) != stdmultiset.end())){ return -1; } }