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 <thiago.macieira@intel.com>
This commit is contained in:
Mårten Nordheim 2020-10-27 08:22:42 +01:00
parent f353519e78
commit d281f5cc35
2 changed files with 132 additions and 0 deletions

View File

@ -697,6 +697,7 @@ class QHash
using Node = QHashPrivate::Node<Key, T>;
using Data = QHashPrivate::Data<Node>;
friend class QSet<Key>;
friend class QMultiHash<Key, T>;
Data *d = nullptr;
@ -1222,6 +1223,11 @@ public:
explicit QMultiHash(const QHash<Key, T> &other)
: QMultiHash(other.begin(), other.end())
{}
explicit QMultiHash(QHash<Key, T> &&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<Key, T> &&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<iterator, iterator> equal_range(const Key &key)
{
detach();

View File

@ -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<int, MyClass> hash;
QMultiHash<int, MyClass> multiHash(std::move(hash));
QVERIFY(multiHash.isEmpty());
}
// QHash is detached
{
MyClass::copies = 0;
MyClass::moves = 0;
QHash<int, MyClass> hash;
hash.emplace(0, "a");
hash.emplace(1, "b");
QMultiHash<int, MyClass> 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<int, MyClass> hash;
hash.emplace(0, "a");
hash.emplace(1, "b");
QHash<int, MyClass> hash2(hash);
QMultiHash<int, MyClass> 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<int, MyClass> hash;
QMultiHash<int, MyClass> multiHash;
multiHash.unite(std::move(hash));
QVERIFY(multiHash.isEmpty());
}
// QHash is detached
{
MyClass::copies = 0;
MyClass::moves = 0;
QHash<int, MyClass> hash;
hash.emplace(0, "a");
hash.emplace(1, "b");
QMultiHash<int, MyClass> 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<int, MyClass> hash;
hash.emplace(0, "a");
hash.emplace(1, "b");
QHash<int, MyClass> hash2(hash);
QMultiHash<int, MyClass> 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<int, MyClass> hash;
hash.emplace(0, "a");
hash.emplace(1, "b");
QMultiHash<int, MyClass> 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 <typename T>
QList<T> sorted(const QList<T> &list)
{