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 <volker.hilsheimer@qt.io>
This commit is contained in:
Marc Mutz 2024-02-06 10:38:55 +01:00
parent 3a2d8c5828
commit e1f45ad818
3 changed files with 42 additions and 0 deletions

View File

@ -5,6 +5,7 @@
#ifndef QMAP_H
#define QMAP_H
#include <QtCore/qhashfunctions.h>
#include <QtCore/qiterator.h>
#include <QtCore/qlist.h>
#include <QtCore/qrefcount.h>
@ -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 <typename M, std::enable_if_t<std::is_same_v<M, QMap>, bool> = true>
friend QtPrivate::QHashMultiReturnType<typename M::key_type, typename M::mapped_type>
# else
using M = QMap;
friend size_t
# endif
qHash(const M &key, size_t seed = 0)
noexcept(QHashPrivate::noexceptPairHash<typename M::key_type, typename M::mapped_type>())
{
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<Key, T> &map, Predicate pred)
return QtPrivate::associative_erase_if(map, pred);
}
//
// QMultiMap
//

View File

@ -1389,3 +1389,12 @@
Returns the number of elements removed, if any.
*/
/*!
\fn template <class Key, class T> QMap<Key, T>::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().
*/

View File

@ -640,16 +640,19 @@ void tst_QMap::operator_eq()
QMap<int, int> 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<QString, QString> 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));
}