diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 781ea1a157..0ce33759d0 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -677,8 +677,10 @@ struct Data return size >= (numBuckets >> 1); } - Bucket findBucket(const Key &key) const noexcept + template Bucket findBucket(const K &key) const noexcept { + static_assert(std::is_same_v, K> || + QHashHeterogeneousSearch, K>::value); Q_ASSERT(numBuckets > 0); size_t hash = QHashPrivate::calculateHash(key, seed); Bucket bucket(this, GrowthPolicy::bucketForHash(numBuckets, hash)); @@ -697,7 +699,7 @@ struct Data } } - Node *findNode(const Key &key) const noexcept + template Node *findNode(const K &key) const noexcept { auto bucket = findBucket(key); if (bucket.isUnused()) @@ -954,6 +956,11 @@ public: } bool remove(const Key &key) + { + return removeImpl(key); + } +private: + template bool removeImpl(const K &key) { if (isEmpty()) // prevents detaching shared null return false; @@ -967,12 +974,20 @@ public: d->erase(it); return true; } + +public: template qsizetype removeIf(Predicate pred) { return QtPrivate::associative_erase_if(*this, pred); } + T take(const Key &key) + { + return takeImpl(key); + } +private: + template T takeImpl(const K &key) { if (isEmpty()) // prevents detaching shared null return T(); @@ -988,6 +1003,7 @@ public: return value; } +public: bool contains(const Key &key) const noexcept { if (!d) @@ -1025,7 +1041,7 @@ public: } private: - template T valueImpl(const Key &key, Fn &&defaultValue) const noexcept + template T valueImpl(const K &key, Fn &&defaultValue) const noexcept { if (d) { Node *n = d->findNode(key); @@ -1240,11 +1256,7 @@ private: return std::make_pair(first, second); } -public: - typedef iterator Iterator; - typedef const_iterator ConstIterator; - inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; } - iterator find(const Key &key) + template iterator findImpl(const K &key) { if (isEmpty()) // prevents detaching shared null return end(); @@ -1256,7 +1268,7 @@ public: return end(); return iterator(it.toIterator(d)); } - const_iterator find(const Key &key) const noexcept + template const_iterator constFindImpl(const K &key) const noexcept { if (isEmpty()) return end(); @@ -1265,6 +1277,19 @@ public: return end(); return const_iterator({d, it.toBucketIndex(d)}); } + +public: + typedef iterator Iterator; + typedef const_iterator ConstIterator; + inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; } + iterator find(const Key &key) + { + return findImpl(key); + } + const_iterator find(const Key &key) const noexcept + { + return constFindImpl(key); + } const_iterator constFind(const Key &key) const noexcept { return find(key); @@ -1328,8 +1353,61 @@ private: result.it.node()->emplaceValue(std::forward(args)...); return iterator(result.it); } -}; +public: +#ifdef __cpp_concepts + bool remove(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + { + return removeImpl(key); + } + T take(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + { + return takeImpl(key); + } + bool contains(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + { + return d ? d->findNode(key) != nullptr : false; + } + qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + { + return contains(key) ? 1 : 0; + } + T value(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + return valueImpl(key, [] { return T(); }); + } + T value(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &defaultValue) const noexcept + { + return valueImpl(key, [&] { return defaultValue; }); + } + const T operator[](const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + return value(key); + } + std::pair + equal_range(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + { + return equal_range_impl(*this, key); + } + std::pair + equal_range(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + return equal_range_impl(*this, key); + } + iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + { + return findImpl(key); + } + const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + return constFindImpl(key); + } + const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + return find(key); + } +#endif // __cpp_concepts +}; template @@ -1506,6 +1584,11 @@ public: } qsizetype remove(const Key &key) + { + return removeImpl(key); + } +private: + template qsizetype removeImpl(const K &key) { if (isEmpty()) // prevents detaching shared null return 0; @@ -1522,12 +1605,20 @@ public: d->erase(it); return n; } + +public: template qsizetype removeIf(Predicate pred) { return QtPrivate::associative_erase_if(*this, pred); } + T take(const Key &key) + { + return takeImpl(key); + } +private: + template T takeImpl(const K &key) { if (isEmpty()) // prevents detaching shared null return T(); @@ -1553,6 +1644,7 @@ public: return t; } +public: bool contains(const Key &key) const noexcept { if (!d) @@ -1586,7 +1678,7 @@ public: } private: - template T valueImpl(const Key &key, Fn &&defaultValue) const noexcept + template T valueImpl(const K &key, Fn &&defaultValue) const noexcept { if (d) { Node *n = d->findNode(key); @@ -1650,8 +1742,14 @@ public: } return res; } + QList values() const { return QList(begin(), end()); } QList values(const Key &key) const + { + return valuesImpl(key); + } +private: + template QList valuesImpl(const K &key) const { QList values; if (d) { @@ -1667,6 +1765,7 @@ public: return values; } +public: class const_iterator; class iterator @@ -1878,7 +1977,9 @@ public: typedef iterator Iterator; typedef const_iterator ConstIterator; inline qsizetype count() const noexcept { return size(); } - iterator find(const Key &key) + +private: + template iterator findImpl(const K &key) { if (isEmpty()) return end(); @@ -1891,11 +1992,7 @@ public: return end(); return iterator(it.toIterator(d)); } - const_iterator find(const Key &key) const noexcept - { - return constFind(key); - } - const_iterator constFind(const Key &key) const noexcept + template const_iterator constFindImpl(const K &key) const noexcept { if (isEmpty()) return end(); @@ -1904,6 +2001,20 @@ public: return constEnd(); return const_iterator(it.toIterator(d)); } +public: + iterator find(const Key &key) + { + return findImpl(key); + } + const_iterator constFind(const Key &key) const noexcept + { + return constFindImpl(key); + } + const_iterator find(const Key &key) const noexcept + { + return constFindImpl(key); + } + iterator insert(const Key &key, const T &value) { return emplace(key, value); @@ -1968,6 +2079,11 @@ public: { QMultiHash result = *this; result += other; return result; } bool contains(const Key &key, const T &value) const noexcept + { + return containsImpl(key, value); + } +private: + template bool containsImpl(const K &key, const T &value) const noexcept { if (isEmpty()) return false; @@ -1977,7 +2093,13 @@ public: return n->value->contains(value); } +public: qsizetype remove(const Key &key, const T &value) + { + return removeImpl(key, value); + } +private: + template qsizetype removeImpl(const K &key, const T &value) { if (isEmpty()) // prevents detaching shared null return 0; @@ -2007,7 +2129,13 @@ public: return n; } +public: qsizetype count(const Key &key) const noexcept + { + return countImpl(key); + } +private: + template qsizetype countImpl(const K &key) const noexcept { if (!d) return 0; @@ -2024,7 +2152,13 @@ public: return n; } +public: qsizetype count(const Key &key, const T &value) const noexcept + { + return countImpl(key, value); + } +private: + template qsizetype countImpl(const K &key, const T &value) const noexcept { if (!d) return 0; @@ -2042,7 +2176,7 @@ public: return n; } - iterator find(const Key &key, const T &value) + template iterator findImpl(const K &key, const T &value) { if (isEmpty()) return end(); @@ -2051,11 +2185,7 @@ public: auto it = constFind(key, value); return iterator(it.i, it.e); } - const_iterator find(const Key &key, const T &value) const noexcept - { - return constFind(key, value); - } - const_iterator constFind(const Key &key, const T &value) const noexcept + template const_iterator constFindImpl(const K &key, const T &value) const noexcept { const_iterator i(constFind(key)); const_iterator end(constEnd()); @@ -2067,6 +2197,21 @@ public: return end; } +public: + iterator find(const Key &key, const T &value) + { + return findImpl(key, value); + } + + const_iterator constFind(const Key &key, const T &value) const noexcept + { + return constFindImpl(key, value); + } + const_iterator find(const Key &key, const T &value) const noexcept + { + return constFind(key, value); + } + QMultiHash &unite(const QMultiHash &other) { if (isEmpty()) { @@ -2103,6 +2248,11 @@ public: } std::pair equal_range(const Key &key) + { + return equal_range_impl(key); + } +private: + template std::pair equal_range_impl(const K &key) { const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach detach(); @@ -2110,7 +2260,13 @@ public: return {iterator(pair.first.i), iterator(pair.second.i)}; } +public: std::pair equal_range(const Key &key) const noexcept + { + return equal_range_impl(key); + } +private: + template std::pair equal_range_impl(const K &key) const noexcept { if (!d) return {end(), end()}; @@ -2124,7 +2280,6 @@ public: return {const_iterator(it), const_iterator(end)}; } -private: void detach_helper() { if (!d) { @@ -2161,6 +2316,90 @@ private: } return iterator(result.it); } + +public: +#ifdef __cpp_concepts + qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + { + return removeImpl(key); + } + T take(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + { + return takeImpl(key); + } + bool contains(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + if (!d) + return false; + return d->findNode(key) != nullptr; + } + T value(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + return valueImpl(key, [] { return T(); }); + } + T value(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &defaultValue) const noexcept + { + return valueImpl(key, [&] { return defaultValue; }); + } + const T operator[](const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + return value(key); + } + QList values(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + { + return valuesImpl(key); + } + iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + { + return findImpl(key); + } + const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + return constFindImpl(key); + } + const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + return constFindImpl(key); + } + bool contains(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) const noexcept + { + return containsImpl(key, value); + } + qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) + { + return removeImpl(key, value); + } + qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + return countImpl(key); + } + qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) const noexcept + { + return countImpl(key, value); + } + iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) + { + return findImpl(key, value); + } + const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) const noexcept + { + return constFindImpl(key, value); + } + const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) const noexcept + { + return constFind(key, value); + } + std::pair + equal_range(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + { + return equal_range_impl(key); + } + std::pair + equal_range(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + { + return equal_range_impl(key); + } +#endif // __cpp_concepts }; Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(Hash) diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index e59ad4de4d..e31c22ab99 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -38,6 +38,11 @@ private slots: void qhash(); void take(); // copied from tst_QMap void operator_eq(); // slightly modified from tst_QMap + void heterogeneousSearch(); + void heterogeneousSearchConstKey(); + void heterogeneousSearchByteArray(); + void heterogeneousSearchString(); + void rehash_isnt_quadratic(); void dont_need_default_constructor(); void qmultihash_specific(); @@ -45,6 +50,10 @@ private slots: void qmultihash_qhash_rvalue_ref_unite(); void qmultihashUnite(); void qmultihashSize(); + void qmultihashHeterogeneousSearch(); + void qmultihashHeterogeneousSearchConstKey(); + void qmultihashHeterogeneousSearchByteArray(); + void qmultihashHeterogeneousSearchString(); void compare(); void compare2(); @@ -1158,6 +1167,187 @@ void tst_QHash::operator_eq() } } +#ifdef __cpp_concepts +struct HeterogeneousHashingType +{ + inline static int conversionCount = 0; + QString s; + + Q_IMPLICIT operator QString() const + { + ++conversionCount; + return s; + } + + // std::equality_comparable_with requires we be self-comparable too + friend bool operator==(const HeterogeneousHashingType &t1, const HeterogeneousHashingType &t2) = default; + + friend bool operator==(const QString &string, const HeterogeneousHashingType &tester) + { return tester.s == string; } + friend bool operator!=(const QString &string, const HeterogeneousHashingType &tester) + { return !(tester.s == string); } + + friend size_t qHash(const HeterogeneousHashingType &tester, size_t seed) + { return qHash(tester.s, seed); } +}; +QT_BEGIN_NAMESPACE +template <> struct QHashHeterogeneousSearch : std::true_type {}; +template <> struct QHashHeterogeneousSearch : std::true_type {}; +QT_END_NAMESPACE +static_assert(std::is_same_v>); +static_assert(std::equality_comparable_with); +static_assert(QHashPrivate::HeterogeneouslySearchableWith); +static_assert(QHashPrivate::HeterogeneouslySearchableWith); + +template struct HeterogeneousSearchTestHelper +{ + static void resetCounter() {} + static void checkCounter() {} +}; +template <> struct HeterogeneousSearchTestHelper +{ + static void resetCounter() + { + HeterogeneousHashingType::conversionCount = 0; + } + static void checkCounter() + { + QTest::setThrowOnFail(true); + auto scopeExit = qScopeGuard([] { QTest::setThrowOnFail(false); }); + QCOMPARE(HeterogeneousHashingType::conversionCount, 0); + } +}; +#else +using HeterogeneousHashingType = QString; +#endif + +template