summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/tools/qcontainertools_impl.h52
-rw-r--r--src/corelib/tools/qhash.cpp58
-rw-r--r--src/corelib/tools/qhash.h22
-rw-r--r--src/corelib/tools/qmap.h24
-rw-r--r--src/corelib/tools/qmap.qdoc29
-rw-r--r--src/corelib/tools/qmultimap.qdoc29
-rw-r--r--tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp67
7 files changed, 281 insertions, 0 deletions
diff --git a/src/corelib/tools/qcontainertools_impl.h b/src/corelib/tools/qcontainertools_impl.h
index 54de6a8815..e4812eefef 100644
--- a/src/corelib/tools/qcontainertools_impl.h
+++ b/src/corelib/tools/qcontainertools_impl.h
@@ -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
diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp
index 8a94695ec3..19fed905dc 100644
--- a/src/corelib/tools/qhash.cpp
+++ b/src/corelib/tools/qhash.cpp
@@ -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
diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h
index 8134f4402c..65ae9b75fd 100644
--- a/src/corelib/tools/qhash.h
+++ b/src/corelib/tools/qhash.h
@@ -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
diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h
index a426a202e1..b64989eadd 100644
--- a/src/corelib/tools/qmap.h
+++ b/src/corelib/tools/qmap.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
diff --git a/src/corelib/tools/qmap.qdoc b/src/corelib/tools/qmap.qdoc
index ace09e7391..3fec85e5b2 100644
--- a/src/corelib/tools/qmap.qdoc
+++ b/src/corelib/tools/qmap.qdoc
@@ -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.
+*/
diff --git a/src/corelib/tools/qmultimap.qdoc b/src/corelib/tools/qmultimap.qdoc
index 487480a927..3cfc6ea727 100644
--- a/src/corelib/tools/qmultimap.qdoc
+++ b/src/corelib/tools/qmultimap.qdoc
@@ -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.
+*/
diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
index 4625353139..08fb9dcdc6 100644
--- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
+++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
@@ -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"