From e1f45ad8187947e243c8247c5cbac2d884d68d55 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 6 Feb 2024 10:38:55 +0100 Subject: [PATCH] QMap: add missing qHash() overload Found in API review, but not deemed important enough to still get into 6.7. Not implementing it for QMultiMap, because there we need to mod out order of equivalent elements. The GHS compiler acted up and tried to compile the noexcept specification, hitting non-existing qHash(QVariant) for QVariantMap and producing a hard error instead of SFINAE'ing out. As a work-around, establish an artificial SFINAE-friendly context, but only for that compiler. [ChangeLog][QtCore][QMap/QHash] Added qHash() overload for QMap. Change-Id: Ia7dbf488e8e5490962118e40581b7c4cc8ed95e5 Reviewed-by: Volker Hilsheimer --- src/corelib/tools/qmap.h | 27 ++++++++++++++++++++++ src/corelib/tools/qmap.qdoc | 9 ++++++++ tests/auto/corelib/tools/qmap/tst_qmap.cpp | 6 +++++ 3 files changed, 42 insertions(+) diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index 436aa18cfd0..6daab908bb6 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -5,6 +5,7 @@ #ifndef QMAP_H #define QMAP_H +#include #include #include #include @@ -777,6 +778,31 @@ public: auto result = d->m.equal_range(akey); return {const_iterator(result.first), const_iterator(result.second)}; } + +private: +#ifdef Q_QDOC + friend size_t qHash(const QMap &key, size_t seed = 0); +#else +# ifdef Q_CC_GHS + // GHS tries to intantiate qHash() for the noexcept running into a + // non-SFINAE'ed hard error... Create an artificial SFINAE context as a + // work-around: + template , bool> = true> + friend QtPrivate::QHashMultiReturnType +# else + using M = QMap; + friend size_t +# endif + qHash(const M &key, size_t seed = 0) + noexcept(QHashPrivate::noexceptPairHash()) + { + if (!key.d) + return seed; + // don't use qHashRange to avoid its compile-time overhead: + return std::accumulate(key.d->m.begin(), key.d->m.end(), seed, + QtPrivate::QHashCombine{}); + } +#endif // !Q_QDOC }; Q_DECLARE_ASSOCIATIVE_ITERATOR(Map) @@ -788,6 +814,7 @@ qsizetype erase_if(QMap &map, Predicate pred) return QtPrivate::associative_erase_if(map, pred); } + // // QMultiMap // diff --git a/src/corelib/tools/qmap.qdoc b/src/corelib/tools/qmap.qdoc index 9b5a8588cf4..d4779ba28c9 100644 --- a/src/corelib/tools/qmap.qdoc +++ b/src/corelib/tools/qmap.qdoc @@ -1389,3 +1389,12 @@ Returns the number of elements removed, if any. */ + +/*! + \fn template QMap::qHash(const QMap &key, size_t seed) + \since 6.8 + + Returns the hash value for \a key, using \a seed to seed the calculation. + + Types \c Key and \c T must be supported by qHash(). +*/ diff --git a/tests/auto/corelib/tools/qmap/tst_qmap.cpp b/tests/auto/corelib/tools/qmap/tst_qmap.cpp index ef2a5383d3c..6950dcf7056 100644 --- a/tests/auto/corelib/tools/qmap/tst_qmap.cpp +++ b/tests/auto/corelib/tools/qmap/tst_qmap.cpp @@ -640,16 +640,19 @@ void tst_QMap::operator_eq() QMap b; QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); a.insert(1,1); b.insert(1,1); QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); a.insert(0,1); b.insert(0,1); QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); // compare for inequality: @@ -672,6 +675,7 @@ void tst_QMap::operator_eq() QMap b; QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); a.insert("Hello", "World"); @@ -680,6 +684,7 @@ void tst_QMap::operator_eq() b.insert("Hello", "World"); QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); a.insert("Goodbye", "cruel world"); @@ -696,6 +701,7 @@ void tst_QMap::operator_eq() // empty keys and null keys match: b.insert(QString(""), QString()); QVERIFY(a == b); + QCOMPARE(qHash(a), qHash(b)); QVERIFY(!(a != b)); }