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 &set, Predicate &pred) return result; } + +// Prerequisite: F is invocable on ArgTypes +template +struct is_invoke_result_explicitly_convertible : std::is_constructible> +{}; + +// is_invocable_r checks for implicit conversions, but we need to check +// for explicit conversions in remove_if. So, roll our own trait. +template +constexpr bool is_invocable_explicit_r_v = std::conjunction_v< + std::is_invocable, + is_invoke_result_explicitly_convertible +>; + +template +auto associative_erase_if(Container &c, Predicate &pred) +{ + // we support predicates callable with either Container::iterator + // or with std::pair + using Iterator = typename Container::iterator; + using Key = typename Container::key_type; + using Value = typename Container::mapped_type; + using KeyValuePair = std::pair; + + 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) { + if (pred(it)) { + it = c.erase(it); + ++result; + } else { + ++it; + } + } else if constexpr (is_invocable_explicit_r_v) { + 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 template qsizetype QHash::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::iterator}, or an argument of type + \c{std::pair}. + + Returns the number of elements removed, if any. + + \sa clear(), take() +*/ + /*! \fn template T QHash::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 template qsizetype QMultiHash::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::iterator}, or an argument of type + \c{std::pair}. + + Returns the number of elements removed, if any. + + \sa clear(), take() +*/ + /*! \fn template T QMultiHash::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 qsizetype erase_if(QHash &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::iterator}, or an argument of type + \c{std::pair}. + + Returns the number of elements removed, if any. +*/ + +/*! \fn template qsizetype erase_if(QMultiHash &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::iterator}, or an argument of type + \c{std::pair}. + + 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 + 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 + 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, size_t seed = 0) return hash; } +template +qsizetype erase_if(QHash &hash, Predicate pred) +{ + return QtPrivate::associative_erase_if(hash, pred); +} + +template +qsizetype erase_if(QMultiHash &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 + 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 +qsizetype erase_if(QMap &map, Predicate pred) +{ + return QtPrivate::associative_erase_if(map, pred); +} + // // QMultiMap // @@ -938,6 +950,12 @@ public: return result; } + template + size_type removeIf(Predicate pred) + { + return QtPrivate::associative_erase_if(*this, pred); + } + T take(const Key &key) { if (!d) @@ -1441,6 +1459,12 @@ QMultiMap operator+=(QMultiMap &lhs, const QMultiMap &rh return lhs.unite(rhs); } +template +qsizetype erase_if(QMultiMap &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 template size_type QMap::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::iterator}, or an argument of type + \c{std::pair}. + + Returns the number of elements removed, if any. + + \sa clear(), take() +*/ + /*! \fn template T QMap::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 qsizetype erase_if(QMap &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::iterator}, or an argument of type + \c{std::pair}. + + 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 template size_type QMultiMap::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::iterator}, or an argument of type + \c{std::pair}. + + Returns the number of elements removed, if any. + + \sa clear(), take() +*/ + /*! \fn template T QMultiMap::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 qsizetype erase_if(QMultiMap &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::iterator}, or an argument of type + \c{std::pair}. + + 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 void erase_if_impl() const; + template + void erase_if_associative_impl() const; + private Q_SLOTS: void erase_QList() { erase_impl>(); } void erase_QVarLengthArray() { erase_impl>(); } @@ -355,6 +358,10 @@ private Q_SLOTS: erase_if_impl>(); #endif } + void erase_if_QMap() { erase_if_associative_impl>(); } + void erase_if_QMultiMap() {erase_if_associative_impl>(); } + void erase_if_QHash() { erase_if_associative_impl>(); } + void erase_if_QMultIHash() { erase_if_associative_impl>(); } }; void tst_ContainerApiSymmetry::init() @@ -647,6 +654,17 @@ Container make(int size) return c; } +template +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 +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; + + auto c = makeAssociative(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(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"