diff options
author | Marc Mutz <marc.mutz@qt.io> | 2022-02-25 08:47:48 +0100 |
---|---|---|
committer | Marc Mutz <marc.mutz@qt.io> | 2022-03-03 00:25:14 +0100 |
commit | 6f5c78fe3d445f1c6c8738f9cedb9dbd847645fa (patch) | |
tree | bdc3d95dee98a54ab84dc30b3f5bd469f06af150 /tests/auto/corelib/tools | |
parent | b2c7f17b940fb7735ff88d9af6e03729a2fdcdd0 (diff) |
QFlatMap: add remove_if
The existing API of QFlatMap did not allow efficient removal of
elements:
- std::remove_if does not apply, because it works by moving elements
back in the range onto those that need to be removed, which doesn't
work in flat_map's case, because, like for all associative
containers, the key in value_type is const.
- The node-based erase-loop (over it = cond ? c.erase(it) :
std::next(it)) works, but, unlike in traditional associative
containers, is quadratic, because flat_map::erase is a linear
operation.
According to Stepanov's principle of Efficient Computational Basis
(Elements of Programming, Section 1.4), we're therefore missing API.
Add it.
I couldn't make up my mind about the calling convention for the
predicate and, despite having authored a merged paper about erase_if,
can never remember what the predicate is supposed to take, so be fancy
and accept all: (*it), (it.key(), it.value()), (it.key()). This means
that unary predicates can either not be generic or must be properly
constrained to distinguish between pair<const K, V> and K, but that's
not necessarily a bad thing.
There's no reason to supply a Qt-ified removeIf on top of the standard
name, because this is private API and doubling the names would do
nothing except double the testing overhead.
Fixes: QTBUG-100983
Change-Id: I12545058958fc5d620baa770f92193c8de8b2d26
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'tests/auto/corelib/tools')
-rw-r--r-- | tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp b/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp index 8674734a11..ae6fcd6cad 100644 --- a/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp +++ b/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp @@ -40,6 +40,20 @@ #include <list> #include <tuple> +static constexpr bool is_even(int n) { return n % 2 == 0; } +static constexpr bool is_empty(QAnyStringView v) { return v.isEmpty(); } + +namespace { +template <typename P> +constexpr inline bool is_pair_impl_v = false; +template <typename T, typename S> +constexpr inline bool is_pair_impl_v<std::pair<T,S>> = true; +template <typename P> +constexpr inline bool is_pair_v = is_pair_impl_v<std::decay_t<P>>; +template <typename P> +using if_pair = std::enable_if_t<is_pair_v<P>, bool>; +} + class tst_QFlatMap : public QObject { Q_OBJECT @@ -53,6 +67,9 @@ private slots: void removal(); void extraction(); void iterators(); + void remove_if_pair() { remove_if_impl([](const auto &p) -> if_pair<decltype(p)> { return is_even(p.first) && is_empty(p.second); }); } + void remove_if_key_value() { remove_if_impl([](const auto &k, const auto &v) { return is_even(k) && is_empty(v); }); } + void remove_if_key() { remove_if_impl([](int k) { return is_even(k); }, true); } void statefulComparator(); void transparency_using(); void transparency_struct(); @@ -63,6 +80,8 @@ private slots: private: template <typename Compare> void transparency_impl(); + template <typename Predicate> + void remove_if_impl(Predicate p, bool removeNonEmptyValues = false); }; #ifdef QFLATMAP_TEMPORARILY_REMOVED @@ -369,6 +388,74 @@ void tst_QFlatMap::iterators() } } +template <typename Pred> +void tst_QFlatMap::remove_if_impl(Pred p, bool removeNonEmptyValues) +{ + // empty stays empty: + { + QFlatMap<int, QString> m; + QCOMPARE(m.remove_if(p), 0); + QVERIFY(m.isEmpty()); + } + // a matching element is removed: + { + { + QFlatMap<int, QString> m; + m.insert_or_assign(0, ""); + QCOMPARE(m.remove_if(p), 1); + QVERIFY(m.isEmpty()); + } + if (removeNonEmptyValues) { + QFlatMap<int, QString> m; + m.insert_or_assign(0, "x"); + QCOMPARE(m.remove_if(p), 1); + QVERIFY(m.isEmpty()); + } + } + // a non-matching element is not removed: + { + { + QFlatMap<int, QString> m; + m.insert_or_assign(1, ""); + QCOMPARE(m.remove_if(p), 0); + QVERIFY(m.contains(1)); + QVERIFY(m[1].isEmpty()); + } + if (removeNonEmptyValues) { + QFlatMap<int, QString> m; + m.insert_or_assign(1, "x"); + QCOMPARE(m.remove_if(p), 0); + QVERIFY(m.contains(1)); + QCOMPARE(m[1], "x"); + } + } + // of matching and non-matching elements, only matching ones are removed: + { + { + QFlatMap<int, QString> m; + m.insert_or_assign(0, ""); + m.insert_or_assign(1, ""); + const auto copy = m; + QCOMPARE(m.remove_if(p), 1); + QCOMPARE(copy.size(), 2); + QCOMPARE(copy[0], ""); + QCOMPARE(copy[1], ""); + QCOMPARE(m.size(), 1); + QVERIFY(m.contains(1)); + QVERIFY(m[1].isEmpty()); + } + { + QFlatMap<int, QString> m; + m.insert_or_assign(1, ""); + m.insert_or_assign(2, ""); + QCOMPARE(m.remove_if(p), 1); + QCOMPARE(m.size(), 1); + QVERIFY(m.contains(1)); + QVERIFY(m[1].isEmpty()); + } + } +} + void tst_QFlatMap::removal() { #ifdef QFLATMAP_TEMPORARILY_REMOVED |