From d281f5cc35a974840441e8ed2587bbe74789e9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Tue, 27 Oct 2020 08:22:42 +0100 Subject: [PATCH] QMultiHash: add a QHash&& constructor and unite() overload Reaches into the internals to avoid erasing one entry at a time from the QHash. Change-Id: I47079592d130d2ecd844998dfa31e633e049d4c1 Reviewed-by: Thiago Macieira --- src/corelib/tools/qhash.h | 19 ++++ tests/auto/corelib/tools/qhash/tst_qhash.cpp | 113 +++++++++++++++++++ 2 files changed, 132 insertions(+) diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 1f0d5d76ade..ce8982416d8 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -697,6 +697,7 @@ class QHash using Node = QHashPrivate::Node; using Data = QHashPrivate::Data; friend class QSet; + friend class QMultiHash; Data *d = nullptr; @@ -1222,6 +1223,11 @@ public: explicit QMultiHash(const QHash &other) : QMultiHash(other.begin(), other.end()) {} + + explicit QMultiHash(QHash &&other) + { + unite(std::move(other)); + } void swap(QMultiHash &other) noexcept { qSwap(d, other.d); qSwap(m_size, other.m_size); } bool operator==(const QMultiHash &other) const noexcept @@ -1815,6 +1821,19 @@ public: return *this; } + QMultiHash &unite(QHash &&other) + { + if (!other.isDetached()) { + unite(other); + return *this; + } + auto it = other.d->begin(); + for (const auto end = other.d->end(); it != end; ++it) + emplace(std::move(it.node()->key), std::move(it.node()->takeValue())); + other.clear(); + return *this; + } + QPair equal_range(const Key &key) { detach(); diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index 3174bd60b86..359095c3d03 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -55,6 +55,8 @@ private slots: void rehash_isnt_quadratic(); void dont_need_default_constructor(); void qmultihash_specific(); + void qmultihash_qhash_rvalue_ref_ctor(); + void qmultihash_qhash_rvalue_ref_unite(); void compare(); void compare2(); @@ -1329,6 +1331,117 @@ void tst_QHash::qmultihash_specific() } } +void tst_QHash::qmultihash_qhash_rvalue_ref_ctor() +{ + // QHash is empty + { + QHash hash; + QMultiHash multiHash(std::move(hash)); + QVERIFY(multiHash.isEmpty()); + } + + // QHash is detached + { + MyClass::copies = 0; + MyClass::moves = 0; + QHash hash; + hash.emplace(0, "a"); + hash.emplace(1, "b"); + QMultiHash multiHash(std::move(hash)); + QCOMPARE(multiHash.size(), 2); + QCOMPARE(multiHash[0].str, QString("a")); + QCOMPARE(multiHash[1].str, QString("b")); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 2); + QCOMPARE(MyClass::count, 2); + } + + // QHash is shared + { + MyClass::copies = 0; + MyClass::moves = 0; + QHash hash; + hash.emplace(0, "a"); + hash.emplace(1, "b"); + QHash hash2(hash); + QMultiHash multiHash(std::move(hash)); + QCOMPARE(multiHash.size(), 2); + QCOMPARE(multiHash[0].str, QString("a")); + QCOMPARE(multiHash[1].str, QString("b")); + QCOMPARE(MyClass::copies, 2); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 4); + } +} + +void tst_QHash::qmultihash_qhash_rvalue_ref_unite() +{ + // QHash is empty + { + QHash hash; + QMultiHash multiHash; + multiHash.unite(std::move(hash)); + QVERIFY(multiHash.isEmpty()); + } + + // QHash is detached + { + MyClass::copies = 0; + MyClass::moves = 0; + QHash hash; + hash.emplace(0, "a"); + hash.emplace(1, "b"); + QMultiHash multiHash; + multiHash.unite(std::move(hash)); + QCOMPARE(multiHash.size(), 2); + QCOMPARE(multiHash[0].str, QString("a")); + QCOMPARE(multiHash[1].str, QString("b")); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 2); + QCOMPARE(MyClass::count, 2); + } + + // QHash is shared + { + MyClass::copies = 0; + MyClass::moves = 0; + QHash hash; + hash.emplace(0, "a"); + hash.emplace(1, "b"); + QHash hash2(hash); + QMultiHash multiHash; + multiHash.unite(std::move(hash)); + QCOMPARE(multiHash.size(), 2); + QCOMPARE(multiHash[0].str, QString("a")); + QCOMPARE(multiHash[1].str, QString("b")); + QCOMPARE(MyClass::copies, 2); + QCOMPARE(MyClass::moves, 0); + QCOMPARE(MyClass::count, 4); + } + + // QMultiHash already contains an item with the same key + { + MyClass::copies = 0; + MyClass::moves = 0; + QHash hash; + hash.emplace(0, "a"); + hash.emplace(1, "b"); + QMultiHash multiHash; + multiHash.emplace(0, "c"); + multiHash.unite(std::move(hash)); + QCOMPARE(multiHash.size(), 3); + const auto aRange = multiHash.equal_range(0); + QCOMPARE(std::distance(aRange.first, aRange.second), 2); + auto it = aRange.first; + QCOMPARE(it->str, QString("a")); + QCOMPARE((++it)->str, QString("c")); + QCOMPARE(multiHash[1].str, QString("b")); + QCOMPARE(MyClass::copies, 0); + QCOMPARE(MyClass::moves, 2); + QCOMPARE(MyClass::count, 3); + } +} + template QList sorted(const QList &list) {