mirror of
https://github.com/boostorg/unordered.git
synced 2025-07-30 19:37:14 +02:00
Merge pull request #105 from cmazakas/feature/erase_if
Implement `erase_if`
This commit is contained in:
@ -11,7 +11,8 @@
|
||||
* Improved {cpp}20 support:
|
||||
** All containers have been updated to support
|
||||
heterogeneous `count`, `equal_range` and `find`.
|
||||
** All containers now implement the member function `contains`
|
||||
** All containers now implement the member function `contains`.
|
||||
** `erase_if` has been implemented for all containers.
|
||||
* Improved {cpp}23 support:
|
||||
** All containers have been updated to support
|
||||
heterogeneous `erase` and `extract`.
|
||||
|
@ -198,6 +198,10 @@ template<class Key, class T, class Hash, class Pred, class Alloc>
|
||||
void xref:#unordered_map_swap_2[swap](unordered_map<Key, T, Hash, Pred, Alloc>& x,
|
||||
unordered_map<Key, T, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)));
|
||||
|
||||
template<class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_map<K, T, H, P, A>::size_type
|
||||
xref:#unordered_map_erase_if[erase_if](unordered_map<K, T, H, P, A>& c, Predicate pred);
|
||||
-----
|
||||
|
||||
---
|
||||
@ -1424,4 +1428,31 @@ Effects:;; `x.swap(y)`
|
||||
Throws:;; Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of `key_equal` or `hasher`.
|
||||
Notes:;; The exception specifications aren't quite the same as the C++11 standard, as the equality predicate and hash function are swapped using their copy constructors.
|
||||
|
||||
---
|
||||
|
||||
=== erase_if
|
||||
```c++
|
||||
template<class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_map<K, T, H, P, A>::size_type
|
||||
erase_if(unordered_map<K, T, H, P, A>& c, Predicate pred);
|
||||
```
|
||||
|
||||
Traverses the container `c` and removes all elements for which the supplied predicate returns `true`.
|
||||
|
||||
[horizontal]
|
||||
Returns:;; The number of erased elements.
|
||||
Notes:;; Equivalent to: +
|
||||
+
|
||||
```c++
|
||||
auto original_size = c.size();
|
||||
for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
if (pred(*i)) {
|
||||
i = c.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
---
|
||||
|
@ -193,6 +193,11 @@ template<class Key, class T, class Hash, class Pred, class Alloc>
|
||||
void xref:#unordered_multimap_swap_2[swap](unordered_multimap<Key, T, Hash, Pred, Alloc>& x,
|
||||
unordered_multimap<Key, T, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)));
|
||||
|
||||
template<class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_multimap<K, T, H, P, A>::size_type
|
||||
xref:#unordered_multimap_erase_if[erase_if](unordered_multimap<K, T, H, P, A>& c, Predicate pred);
|
||||
|
||||
-----
|
||||
|
||||
---
|
||||
@ -1368,4 +1373,31 @@ Effects:;; `x.swap(y)`
|
||||
Throws:;; Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of `key_equal` or `hasher`.
|
||||
Notes:;; The exception specifications aren't quite the same as the C++11 standard, as the equality predicate and hash function are swapped using their copy constructors.
|
||||
|
||||
---
|
||||
|
||||
=== erase_if
|
||||
```c++
|
||||
template<class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_multimap<K, T, H, P, A>::size_type
|
||||
erase_if(unordered_multimap<K, T, H, P, A>& c, Predicate pred);
|
||||
```
|
||||
|
||||
Traverses the container `c` and removes all elements for which the supplied predicate returns `true`.
|
||||
|
||||
[horizontal]
|
||||
Returns:;; The number of erased elements.
|
||||
Notes:;; Equivalent to: +
|
||||
+
|
||||
```c++
|
||||
auto original_size = c.size();
|
||||
for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
if (pred(*i)) {
|
||||
i = c.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
---
|
||||
|
@ -187,6 +187,10 @@ template<class Key, class Hash, class Pred, class Alloc>
|
||||
void xref:#unordered_multiset_swap_2[swap](unordered_multiset<Key, Hash, Pred, Alloc>& x,
|
||||
unordered_multiset<Key, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)));
|
||||
|
||||
template<class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_multiset<K, H, P, A>::size_type
|
||||
xref:#unordered_multiset_erase_if[erase_if](unordered_multiset<K, H, P, A>& c, Predicate pred);
|
||||
-----
|
||||
|
||||
---
|
||||
@ -1326,4 +1330,31 @@ Effects:;; `x.swap(y)`
|
||||
Throws:;; Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of `key_equal` or `hasher`.
|
||||
Notes:;; The exception specifications aren't quite the same as the C++11 standard, as the equality predicate and hash function are swapped using their copy constructors.
|
||||
|
||||
---
|
||||
|
||||
=== erase_if
|
||||
```c++
|
||||
template<class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_multiset<K, H, P, A>::size_type
|
||||
erase_if(unordered_multiset<K, H, P, A>& c, Predicate pred);
|
||||
```
|
||||
|
||||
Traverses the container `c` and removes all elements for which the supplied predicate returns `true`.
|
||||
|
||||
[horizontal]
|
||||
Returns:;; The number of erased elements.
|
||||
Notes:;; Equivalent to: +
|
||||
+
|
||||
```c++
|
||||
auto original_size = c.size();
|
||||
for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
if (pred(*i)) {
|
||||
i = c.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
---
|
||||
|
@ -188,6 +188,10 @@ template<class Key, class Hash, class Pred, class Alloc>
|
||||
void xref:#unordered_set_swap_2[swap](unordered_set<Key, Hash, Pred, Alloc>& x,
|
||||
unordered_set<Key, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)));
|
||||
|
||||
template<class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_set<K, H, P, A>::size_type
|
||||
xref:#unordered_set_erase_if[erase_if](unordered_set<K, H, P, A>& c, Predicate pred);
|
||||
-----
|
||||
|
||||
---
|
||||
@ -1349,3 +1353,30 @@ Throws:;; Doesn't throw an exception unless it is thrown by the copy constructor
|
||||
Notes:;; The exception specifications aren't quite the same as the C++11 standard, as the equality predicate and hash function are swapped using their copy constructors.
|
||||
|
||||
---
|
||||
|
||||
=== erase_if
|
||||
```c++
|
||||
template<class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_set<K, H, P, A>::size_type
|
||||
erase_if(unordered_set<K, H, P, A>& c, Predicate pred);
|
||||
```
|
||||
|
||||
Traverses the container `c` and removes all elements for which the supplied predicate returns `true`.
|
||||
|
||||
[horizontal]
|
||||
Returns:;; The number of erased elements.
|
||||
Notes:;; Equivalent to: +
|
||||
+
|
||||
```c++
|
||||
auto original_size = c.size();
|
||||
for (auto i = c.begin(), last = c.end(); i != last; ) {
|
||||
if (pred(*i)) {
|
||||
i = c.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return original_size - c.size();
|
||||
```
|
||||
|
||||
---
|
||||
|
@ -4360,6 +4360,25 @@ namespace boost {
|
||||
typedef typename pick::bucket bucket;
|
||||
typedef typename pick::link_pointer link_pointer;
|
||||
};
|
||||
|
||||
template <class Container, class Predicate>
|
||||
typename Container::size_type erase_if(Container& c, Predicate& pred)
|
||||
{
|
||||
typedef typename Container::size_type size_type;
|
||||
typedef typename Container::iterator iterator;
|
||||
|
||||
size_type const size = c.size();
|
||||
|
||||
for (iterator pos = c.begin(), end = c.end(); pos != end;) {
|
||||
if (pred(*pos)) {
|
||||
pos = c.erase(pos);
|
||||
} else {
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
return (size - c.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2168,6 +2168,13 @@ namespace boost {
|
||||
m1.swap(m2);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_map<K, T, H, P, A>::size_type erase_if(
|
||||
unordered_map<K, T, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return detail::erase_if(c, pred);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class K, class T, class H, class P, class A>
|
||||
@ -2613,6 +2620,13 @@ namespace boost {
|
||||
m1.swap(m2);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_multimap<K, T, H, P, A>::size_type erase_if(
|
||||
unordered_multimap<K, T, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return detail::erase_if(c, pred);
|
||||
}
|
||||
|
||||
template <typename N, class K, class T, class A> class node_handle_map
|
||||
{
|
||||
BOOST_MOVABLE_BUT_NOT_COPYABLE(node_handle_map)
|
||||
|
@ -34,6 +34,10 @@ namespace boost {
|
||||
unordered_map<K, T, H, P, A>& m1, unordered_map<K, T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_map<K, T, H, P, A>::size_type erase_if(
|
||||
unordered_map<K, T, H, P, A>& c, Predicate pred);
|
||||
|
||||
template <class K, class T, class H = boost::hash<K>,
|
||||
class P = std::equal_to<K>,
|
||||
class A = std::allocator<std::pair<const K, T> > >
|
||||
@ -50,6 +54,10 @@ namespace boost {
|
||||
unordered_multimap<K, T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename unordered_multimap<K, T, H, P, A>::size_type erase_if(
|
||||
unordered_multimap<K, T, H, P, A>& c, Predicate pred);
|
||||
|
||||
template <class N, class K, class T, class A> class node_handle_map;
|
||||
template <class N, class K, class T, class A> struct insert_return_type_map;
|
||||
}
|
||||
|
@ -1676,6 +1676,13 @@ namespace boost {
|
||||
m1.swap(m2);
|
||||
}
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_set<K, H, P, A>::size_type erase_if(
|
||||
unordered_set<K, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return detail::erase_if(c, pred);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class T, class H, class P, class A>
|
||||
@ -2079,6 +2086,13 @@ namespace boost {
|
||||
m1.swap(m2);
|
||||
}
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_multiset<K, H, P, A>::size_type erase_if(
|
||||
unordered_multiset<K, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return detail::erase_if(c, pred);
|
||||
}
|
||||
|
||||
template <typename N, typename T, typename A> class node_handle_set
|
||||
{
|
||||
BOOST_MOVABLE_BUT_NOT_COPYABLE(node_handle_set)
|
||||
|
@ -33,6 +33,10 @@ namespace boost {
|
||||
unordered_set<T, H, P, A>& m1, unordered_set<T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_set<K, H, P, A>::size_type erase_if(
|
||||
unordered_set<K, H, P, A>& c, Predicate pred);
|
||||
|
||||
template <class T, class H = boost::hash<T>, class P = std::equal_to<T>,
|
||||
class A = std::allocator<T> >
|
||||
class unordered_multiset;
|
||||
@ -48,6 +52,10 @@ namespace boost {
|
||||
unordered_multiset<T, H, P, A>& m1, unordered_multiset<T, H, P, A>& m2)
|
||||
BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.swap(m2)));
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename unordered_multiset<K, H, P, A>::size_type erase_if(
|
||||
unordered_multiset<K, H, P, A>& c, Predicate pred);
|
||||
|
||||
template <class N, class T, class A> class node_handle_set;
|
||||
template <class N, class T, class A> struct insert_return_type_set;
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ test-suite unordered
|
||||
[ run unordered/reserve_tests.cpp ]
|
||||
[ run unordered/contains_tests.cpp ]
|
||||
[ run unordered/mix_policy.cpp ]
|
||||
[ run unordered/erase_if.cpp ]
|
||||
|
||||
[ run unordered/compile_set.cpp : :
|
||||
: <define>BOOST_UNORDERED_USE_MOVE
|
||||
|
168
test/unordered/erase_if.cpp
Normal file
168
test/unordered/erase_if.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright 2021 Christian Mazakas.
|
||||
// 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)
|
||||
|
||||
// clang-format off
|
||||
#include "../helpers/prefix.hpp"
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "../helpers/postfix.hpp"
|
||||
// clang-format on
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#if BOOST_CXX_VERSION >= 201103L
|
||||
#define UNORDERED_LVALUE_QUAL &
|
||||
#else
|
||||
#define UNORDERED_LVALUE_QUAL
|
||||
#endif
|
||||
|
||||
namespace test {
|
||||
struct is_even
|
||||
{
|
||||
is_even() {}
|
||||
|
||||
#if BOOST_CXX_VERSION >= 201703L
|
||||
// immovable for C++17
|
||||
is_even(is_even const&) = delete;
|
||||
is_even(is_even&&) = delete;
|
||||
|
||||
is_even& operator=(is_even const&) = delete;
|
||||
is_even& operator=(is_even&&) = delete;
|
||||
#elif BOOST_CXX_VERSION >= 201103L
|
||||
// move-only for C++11
|
||||
is_even(is_even const&) = delete;
|
||||
is_even(is_even&&) = default;
|
||||
|
||||
is_even& operator=(is_even const&) = delete;
|
||||
is_even& operator=(is_even&&) = default;
|
||||
#else
|
||||
// copyable otherwise
|
||||
is_even(is_even const&) {}
|
||||
is_even& operator=(is_even const&) { return *this; }
|
||||
#endif
|
||||
|
||||
bool operator()(
|
||||
std::pair<std::string const, int>& key_value) UNORDERED_LVALUE_QUAL
|
||||
{
|
||||
int const v = key_value.second;
|
||||
return (v % 2 == 0);
|
||||
}
|
||||
|
||||
bool operator()(int const& value) UNORDERED_LVALUE_QUAL
|
||||
{
|
||||
int const v = value;
|
||||
return (v % 2 == 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct is_too_large
|
||||
{
|
||||
is_too_large() {}
|
||||
|
||||
#if BOOST_CXX_VERSION >= 201703L
|
||||
// immovable for C++17
|
||||
is_too_large(is_too_large const&) = delete;
|
||||
is_too_large(is_too_large&&) = delete;
|
||||
|
||||
is_too_large& operator=(is_too_large const&) = delete;
|
||||
is_too_large& operator=(is_too_large&&) = delete;
|
||||
#elif BOOST_CXX_VERSION >= 201103L
|
||||
// move-only for C++11
|
||||
is_too_large(is_too_large const&) = delete;
|
||||
is_too_large(is_too_large&&) = default;
|
||||
|
||||
is_too_large& operator=(is_too_large const&) = delete;
|
||||
is_too_large& operator=(is_too_large&&) = default;
|
||||
#else
|
||||
// copyable otherwise
|
||||
is_too_large(is_too_large const&) {}
|
||||
|
||||
is_too_large& operator=(is_too_large const&) { return *this; }
|
||||
#endif
|
||||
|
||||
bool operator()(
|
||||
std::pair<std::string const, int>& key_value) UNORDERED_LVALUE_QUAL
|
||||
{
|
||||
int const v = key_value.second;
|
||||
return v >= 1000;
|
||||
}
|
||||
|
||||
bool operator()(int const& value) UNORDERED_LVALUE_QUAL
|
||||
{
|
||||
int const v = value;
|
||||
return v >= 1000;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
template <class UnorderedMap> void test_map_erase_if()
|
||||
{
|
||||
typedef UnorderedMap map_type;
|
||||
typedef typename map_type::size_type size_type;
|
||||
|
||||
map_type map;
|
||||
size_type num_erased = erase_if(map, test::is_even());
|
||||
BOOST_TEST(map.empty());
|
||||
BOOST_TEST_EQ(num_erased, 0u);
|
||||
|
||||
map.emplace("a", 1);
|
||||
map.emplace("b", 2);
|
||||
map.emplace("b", 4);
|
||||
map.emplace("b", 8);
|
||||
map.emplace("b", 16);
|
||||
map.emplace("c", 3);
|
||||
|
||||
size_type size = map.size();
|
||||
|
||||
num_erased = erase_if(map, test::is_too_large());
|
||||
|
||||
BOOST_TEST_EQ(map.size(), size);
|
||||
BOOST_TEST_EQ(num_erased, 0u);
|
||||
|
||||
num_erased = erase_if(map, test::is_even());
|
||||
BOOST_TEST_EQ(map.size(), 2u);
|
||||
BOOST_TEST_EQ(num_erased, size - map.size());
|
||||
}
|
||||
|
||||
template <class UnorderedSet> void test_set_erase_if()
|
||||
{
|
||||
typedef UnorderedSet set_type;
|
||||
typedef typename set_type::size_type size_type;
|
||||
|
||||
set_type set;
|
||||
size_type num_erased = erase_if(set, test::is_even());
|
||||
BOOST_TEST(set.empty());
|
||||
BOOST_TEST_EQ(num_erased, 0u);
|
||||
|
||||
set.emplace(1);
|
||||
set.emplace(2);
|
||||
set.emplace(2);
|
||||
set.emplace(2);
|
||||
set.emplace(2);
|
||||
set.emplace(3);
|
||||
|
||||
size_type size = set.size();
|
||||
|
||||
num_erased = erase_if(set, test::is_too_large());
|
||||
|
||||
BOOST_TEST_EQ(set.size(), size);
|
||||
BOOST_TEST_EQ(num_erased, 0u);
|
||||
|
||||
num_erased = erase_if(set, test::is_even());
|
||||
BOOST_TEST_EQ(set.size(), 2u);
|
||||
BOOST_TEST_EQ(num_erased, size - set.size());
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (unordered_erase_if) {
|
||||
test_map_erase_if<boost::unordered_map<std::string, int> >();
|
||||
test_map_erase_if<boost::unordered_multimap<std::string, int> >();
|
||||
|
||||
test_set_erase_if<boost::unordered_set<int> >();
|
||||
test_set_erase_if<boost::unordered_multiset<int> >();
|
||||
}
|
||||
|
||||
RUN_TESTS()
|
Reference in New Issue
Block a user