Associative containers: add erase_if
Use a trick similar to the one we use for their ranged constructors: support predicates that either take a container's iterator, or that take a std::pair (for STL compatibility). [ChangeLog][QtCore][QMap] Added removeIf() and erase_if(). [ChangeLog][QtCore][QMultiMap] Added removeIf() and erase_if(). [ChangeLog][QtCore][QHash] Added removeIf() and erase_if(). [ChangeLog][QtCore][QMultiHash] Added removeIf() and erase_if(). Change-Id: Ie40aadf6217d7a4126a626c390d530812ebcf020 Reviewed-by: Andrei Golubev <andrei.golubev@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
9341f2e3d0
commit
e12e2b43b7
@ -215,6 +215,58 @@ qsizetype qset_erase_if(QSet<T> &set, Predicate &pred)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Prerequisite: F is invocable on ArgTypes
|
||||
template <typename R, typename F, typename ... ArgTypes>
|
||||
struct is_invoke_result_explicitly_convertible : std::is_constructible<R, std::invoke_result_t<F, ArgTypes...>>
|
||||
{};
|
||||
|
||||
// is_invocable_r checks for implicit conversions, but we need to check
|
||||
// for explicit conversions in remove_if. So, roll our own trait.
|
||||
template <typename R, typename F, typename ... ArgTypes>
|
||||
constexpr bool is_invocable_explicit_r_v = std::conjunction_v<
|
||||
std::is_invocable<F, ArgTypes...>,
|
||||
is_invoke_result_explicitly_convertible<R, F, ArgTypes...>
|
||||
>;
|
||||
|
||||
template <typename Container, typename Predicate>
|
||||
auto associative_erase_if(Container &c, Predicate &pred)
|
||||
{
|
||||
// we support predicates callable with either Container::iterator
|
||||
// or with std::pair<const Key &, Value &>
|
||||
using Iterator = typename Container::iterator;
|
||||
using Key = typename Container::key_type;
|
||||
using Value = typename Container::mapped_type;
|
||||
using KeyValuePair = std::pair<const Key &, Value &>;
|
||||
|
||||
typename Container::size_type result = 0;
|
||||
|
||||
auto it = c.begin();
|
||||
const auto e = c.end();
|
||||
while (it != e) {
|
||||
if constexpr (is_invocable_explicit_r_v<bool, Predicate &, Iterator &>) {
|
||||
if (pred(it)) {
|
||||
it = c.erase(it);
|
||||
++result;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
} else if constexpr (is_invocable_explicit_r_v<bool, Predicate &, KeyValuePair &&>) {
|
||||
KeyValuePair p(it.key(), it.value());
|
||||
if (pred(std::move(p))) {
|
||||
it = c.erase(it);
|
||||
++result;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
} else {
|
||||
static_assert(sizeof(Container) == 0, "Predicate has an incompatible signature");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace QtPrivate
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -1521,6 +1521,21 @@ size_t qHash(long double key, size_t seed) noexcept
|
||||
\sa clear(), take()
|
||||
*/
|
||||
|
||||
/*! \fn template <class Key, class T> template <typename Predicate> qsizetype QHash<Key, T>::removeIf(Predicate pred)
|
||||
\since 6.1
|
||||
|
||||
Removes all elements for which the predicate \a pred returns true
|
||||
from the hash.
|
||||
|
||||
The function supports predicates which take either an argument of
|
||||
type \c{QHash<Key, T>::iterator}, or an argument of type
|
||||
\c{std::pair<const Key &, T &>}.
|
||||
|
||||
Returns the number of elements removed, if any.
|
||||
|
||||
\sa clear(), take()
|
||||
*/
|
||||
|
||||
/*! \fn template <class Key, class T> T QHash<Key, T>::take(const Key &key)
|
||||
|
||||
Removes the item with the \a key from the hash and returns
|
||||
@ -2651,6 +2666,21 @@ size_t qHash(long double key, size_t seed) noexcept
|
||||
\sa remove()
|
||||
*/
|
||||
|
||||
/*! \fn template <class Key, class T> template <typename Predicate> qsizetype QMultiHash<Key, T>::removeIf(Predicate pred)
|
||||
\since 6.1
|
||||
|
||||
Removes all elements for which the predicate \a pred returns true
|
||||
from the multi hash.
|
||||
|
||||
The function supports predicates which take either an argument of
|
||||
type \c{QMultiHash<Key, T>::iterator}, or an argument of type
|
||||
\c{std::pair<const Key &, T &>}.
|
||||
|
||||
Returns the number of elements removed, if any.
|
||||
|
||||
\sa clear(), take()
|
||||
*/
|
||||
|
||||
/*! \fn template <class Key, class T> T QMultiHash<Key, T>::take(const Key &key)
|
||||
|
||||
Removes the item with the \a key from the hash and returns
|
||||
@ -3338,4 +3368,32 @@ size_t qHash(long double key, size_t seed) noexcept
|
||||
Type \c T must be supported by qHash().
|
||||
*/
|
||||
|
||||
/*! \fn template <typename Key, typename T, typename Predicate> qsizetype erase_if(QHash<Key, T> &hash, Predicate pred)
|
||||
\relates QHash
|
||||
\since 6.1
|
||||
|
||||
Removes all elements for which the predicate \a pred returns true
|
||||
from the hash \a hash.
|
||||
|
||||
The function supports predicates which take either an argument of
|
||||
type \c{QHash<Key, T>::iterator}, or an argument of type
|
||||
\c{std::pair<const Key &, T &>}.
|
||||
|
||||
Returns the number of elements removed, if any.
|
||||
*/
|
||||
|
||||
/*! \fn template <typename Key, typename T, typename Predicate> qsizetype erase_if(QMultiHash<Key, T> &hash, Predicate pred)
|
||||
\relates QMultiHash
|
||||
\since 6.1
|
||||
|
||||
Removes all elements for which the predicate \a pred returns true
|
||||
from the multi hash \a hash.
|
||||
|
||||
The function supports predicates which take either an argument of
|
||||
type \c{QMultiHash<Key, T>::iterator}, or an argument of type
|
||||
\c{std::pair<const Key &, T &>}.
|
||||
|
||||
Returns the number of elements removed, if any.
|
||||
*/
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -872,6 +872,11 @@ public:
|
||||
d->erase(it);
|
||||
return true;
|
||||
}
|
||||
template <typename Predicate>
|
||||
qsizetype removeIf(Predicate pred)
|
||||
{
|
||||
return QtPrivate::associative_erase_if(*this, pred);
|
||||
}
|
||||
T take(const Key &key)
|
||||
{
|
||||
if (isEmpty()) // prevents detaching shared null
|
||||
@ -1354,6 +1359,11 @@ public:
|
||||
d->erase(it);
|
||||
return n;
|
||||
}
|
||||
template <typename Predicate>
|
||||
qsizetype removeIf(Predicate pred)
|
||||
{
|
||||
return QtPrivate::associative_erase_if(*this, pred);
|
||||
}
|
||||
T take(const Key &key)
|
||||
{
|
||||
if (isEmpty()) // prevents detaching shared null
|
||||
@ -1946,6 +1956,18 @@ inline size_t qHash(const QMultiHash<Key, T> &key, size_t seed = 0)
|
||||
return hash;
|
||||
}
|
||||
|
||||
template <typename Key, typename T, typename Predicate>
|
||||
qsizetype erase_if(QHash<Key, T> &hash, Predicate pred)
|
||||
{
|
||||
return QtPrivate::associative_erase_if(hash, pred);
|
||||
}
|
||||
|
||||
template <typename Key, typename T, typename Predicate>
|
||||
qsizetype erase_if(QMultiHash<Key, T> &hash, Predicate pred)
|
||||
{
|
||||
return QtPrivate::associative_erase_if(hash, pred);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHASH_H
|
||||
|
@ -343,6 +343,12 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Predicate>
|
||||
size_type removeIf(Predicate pred)
|
||||
{
|
||||
return QtPrivate::associative_erase_if(*this, pred);
|
||||
}
|
||||
|
||||
T take(const Key &key)
|
||||
{
|
||||
if (!d)
|
||||
@ -742,6 +748,12 @@ public:
|
||||
Q_DECLARE_ASSOCIATIVE_ITERATOR(Map)
|
||||
Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(Map)
|
||||
|
||||
template <typename Key, typename T, typename Predicate>
|
||||
qsizetype erase_if(QMap<Key, T> &map, Predicate pred)
|
||||
{
|
||||
return QtPrivate::associative_erase_if(map, pred);
|
||||
}
|
||||
|
||||
//
|
||||
// QMultiMap
|
||||
//
|
||||
@ -938,6 +950,12 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Predicate>
|
||||
size_type removeIf(Predicate pred)
|
||||
{
|
||||
return QtPrivate::associative_erase_if(*this, pred);
|
||||
}
|
||||
|
||||
T take(const Key &key)
|
||||
{
|
||||
if (!d)
|
||||
@ -1441,6 +1459,12 @@ QMultiMap<Key, T> operator+=(QMultiMap<Key, T> &lhs, const QMultiMap<Key, T> &rh
|
||||
return lhs.unite(rhs);
|
||||
}
|
||||
|
||||
template <typename Key, typename T, typename Predicate>
|
||||
qsizetype erase_if(QMultiMap<Key, T> &map, Predicate pred)
|
||||
{
|
||||
return QtPrivate::associative_erase_if(map, pred);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QMAP_H
|
||||
|
@ -334,6 +334,21 @@
|
||||
\sa clear(), take()
|
||||
*/
|
||||
|
||||
/*! \fn template <class Key, class T> template <typename Predicate> size_type QMap<Key, T>::removeIf(Predicate pred)
|
||||
\since 6.1
|
||||
|
||||
Removes all elements for which the predicate \a pred returns true
|
||||
from the map.
|
||||
|
||||
The function supports predicates which take either an argument of
|
||||
type \c{QMap<Key, T>::iterator}, or an argument of type
|
||||
\c{std::pair<const Key &, T &>}.
|
||||
|
||||
Returns the number of elements removed, if any.
|
||||
|
||||
\sa clear(), take()
|
||||
*/
|
||||
|
||||
/*! \fn template <class Key, class T> T QMap<Key, T>::take(const Key &key)
|
||||
|
||||
Removes the item with the key \a key from the map and returns
|
||||
@ -1372,3 +1387,17 @@
|
||||
|
||||
\sa{Serializing Qt Data Types}{Format of the QDataStream operators}
|
||||
*/
|
||||
|
||||
/*! \fn template <typename Key, typename T, typename Predicate> qsizetype erase_if(QMap<Key, T> &map, Predicate pred)
|
||||
\relates QMap
|
||||
\since 6.1
|
||||
|
||||
Removes all elements for which the predicate \a pred returns true
|
||||
from the map \a map.
|
||||
|
||||
The function supports predicates which take either an argument of
|
||||
type \c{QMap<Key, T>::iterator}, or an argument of type
|
||||
\c{std::pair<const Key &, T &>}.
|
||||
|
||||
Returns the number of elements removed, if any.
|
||||
*/
|
||||
|
@ -352,6 +352,21 @@
|
||||
\sa clear(), take()
|
||||
*/
|
||||
|
||||
/*! \fn template <class Key, class T> template <typename Predicate> size_type QMultiMap<Key, T>::removeIf(Predicate pred)
|
||||
\since 6.1
|
||||
|
||||
Removes all elements for which the predicate \a pred returns true
|
||||
from the multi map.
|
||||
|
||||
The function supports predicates which take either an argument of
|
||||
type \c{QMultiMap<Key, T>::iterator}, or an argument of type
|
||||
\c{std::pair<const Key &, T &>}.
|
||||
|
||||
Returns the number of elements removed, if any.
|
||||
|
||||
\sa clear(), take()
|
||||
*/
|
||||
|
||||
/*! \fn template <class Key, class T> T QMultiMap<Key, T>::take(const Key &key)
|
||||
|
||||
Removes the item with the key \a key from the multi map and returns
|
||||
@ -1508,3 +1523,17 @@
|
||||
|
||||
\sa{Serializing Qt Data Types}{Format of the QDataStream operators}
|
||||
*/
|
||||
|
||||
/*! \fn template <typename Key, typename T, typename Predicate> qsizetype erase_if(QMultiMap<Key, T> &map, Predicate pred)
|
||||
\relates QMultiMap
|
||||
\since 6.1
|
||||
|
||||
Removes all elements for which the predicate \a pred returns true
|
||||
from the multi map \a map.
|
||||
|
||||
The function supports predicates which take either an argument of
|
||||
type \c{QMultiMap<Key, T>::iterator}, or an argument of type
|
||||
\c{std::pair<const Key &, T &>}.
|
||||
|
||||
Returns the number of elements removed, if any.
|
||||
*/
|
||||
|
@ -334,6 +334,9 @@ private:
|
||||
template <typename Container>
|
||||
void erase_if_impl() const;
|
||||
|
||||
template <typename Container>
|
||||
void erase_if_associative_impl() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void erase_QList() { erase_impl<QList<int>>(); }
|
||||
void erase_QVarLengthArray() { erase_impl<QVarLengthArray<int>>(); }
|
||||
@ -355,6 +358,10 @@ private Q_SLOTS:
|
||||
erase_if_impl<std::vector<int>>();
|
||||
#endif
|
||||
}
|
||||
void erase_if_QMap() { erase_if_associative_impl<QMap<int, int>>(); }
|
||||
void erase_if_QMultiMap() {erase_if_associative_impl<QMultiMap<int, int>>(); }
|
||||
void erase_if_QHash() { erase_if_associative_impl<QHash<int, int>>(); }
|
||||
void erase_if_QMultIHash() { erase_if_associative_impl<QMultiHash<int, int>>(); }
|
||||
};
|
||||
|
||||
void tst_ContainerApiSymmetry::init()
|
||||
@ -647,6 +654,17 @@ Container make(int size)
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
Container makeAssociative(int size)
|
||||
{
|
||||
using K = typename Container::key_type;
|
||||
using V = typename Container::mapped_type;
|
||||
Container c;
|
||||
for (int i = 1; i <= size; ++i)
|
||||
c.insert(K(i), V(i));
|
||||
return c;
|
||||
}
|
||||
|
||||
static QString s_string = QStringLiteral("\1\2\3\4\5\6\7");
|
||||
|
||||
template <> QString make(int size) { return s_string.left(size); }
|
||||
@ -728,5 +746,54 @@ void tst_ContainerApiSymmetry::erase_if_impl() const
|
||||
QCOMPARE(c.size(), S(0));
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
void tst_ContainerApiSymmetry::erase_if_associative_impl() const
|
||||
{
|
||||
using S = typename Container::size_type;
|
||||
using K = typename Container::key_type;
|
||||
using V = typename Container::mapped_type;
|
||||
using I = typename Container::iterator;
|
||||
using P = std::pair<const K &, V &>;
|
||||
|
||||
auto c = makeAssociative<Container>(20);
|
||||
QCOMPARE(c.size(), S(20));
|
||||
|
||||
auto result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 2 == 0; });
|
||||
QCOMPARE(result, S(10));
|
||||
QCOMPARE(c.size(), S(10));
|
||||
|
||||
result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 3 == 0; });
|
||||
QCOMPARE(result, S(3));
|
||||
QCOMPARE(c.size(), S(7));
|
||||
|
||||
result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 42 == 0; });
|
||||
QCOMPARE(result, S(0));
|
||||
QCOMPARE(c.size(), S(7));
|
||||
|
||||
result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 2 == 1; });
|
||||
QCOMPARE(result, S(7));
|
||||
QCOMPARE(c.size(), S(0));
|
||||
|
||||
// same, but with a predicate taking a Qt iterator
|
||||
c = makeAssociative<Container>(20);
|
||||
QCOMPARE(c.size(), S(20));
|
||||
|
||||
result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 2 == 0; });
|
||||
QCOMPARE(result, S(10));
|
||||
QCOMPARE(c.size(), S(10));
|
||||
|
||||
result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 3 == 0; });
|
||||
QCOMPARE(result, S(3));
|
||||
QCOMPARE(c.size(), S(7));
|
||||
|
||||
result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 42 == 0; });
|
||||
QCOMPARE(result, S(0));
|
||||
QCOMPARE(c.size(), S(7));
|
||||
|
||||
result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 2 == 1; });
|
||||
QCOMPARE(result, S(7));
|
||||
QCOMPARE(c.size(), S(0));
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_ContainerApiSymmetry)
|
||||
#include "tst_containerapisymmetry.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user