diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp index f1be49c1d4..b7e56c5ec3 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp @@ -297,11 +297,11 @@ inline size_t qHash(const std::unordered_set &key, size_t seed = 0) //! [31] //! [32] -size_t qHash(K key); -size_t qHash(const K &key); - size_t qHash(K key, size_t seed); size_t qHash(const K &key, size_t seed); + +size_t qHash(K key); // deprecated, do not use +size_t qHash(const K &key); // deprecated, do not use //! [32] //! [33] diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 2d2061c707..7ff53fe67e 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -1683,10 +1683,15 @@ size_t qHash(long double key, size_t seed) noexcept The two-arguments overloads take an unsigned integer that should be used to seed the calculation of the hash function. This seed is provided by QHash - in order to prevent a family of \l{algorithmic complexity attacks}. If both - a one-argument and a two-arguments overload are defined for a key type, - the latter is used by QHash (note that you can simply define a - two-arguments version, and use a default value for the seed parameter). + in order to prevent a family of \l{algorithmic complexity attacks}. + + \note In Qt 6 it is possible to define a \c{qHash()} overload + taking only one argument; support for this is deprecated. Starting + with Qt 7, it will be mandatory to use a two-arguments overload. If + both a one-argument and a two-arguments overload are defined for a + key type, the latter is used by QHash (note that you can simply + define a two-arguments version, and use a default value for the + seed parameter). The second way to provide a hashing function is by specializing the \c{std::hash} class for the key type \c{K}, and providing a diff --git a/src/corelib/tools/qhashfunctions.h b/src/corelib/tools/qhashfunctions.h index 029d98674c..bcb8caf1f7 100644 --- a/src/corelib/tools/qhashfunctions.h +++ b/src/corelib/tools/qhashfunctions.h @@ -68,14 +68,6 @@ Q_DECL_CONST_FUNCTION constexpr size_t hash(size_t key, size_t seed) noexcept } } -template -constexpr inline bool HasQHashSingleArgOverload = false; - -template -constexpr inline bool HasQHashSingleArgOverload())), size_t> ->> = true; - template static constexpr bool noexceptPairHash(); } @@ -141,6 +133,9 @@ Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(std::nullptr_t, size_t seed { return seed; } +template , bool> = true> +Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(Enum e, size_t seed = 0) noexcept +{ return QHashPrivate::hash(qToUnderlying(e), seed); } // (some) Qt types Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(const QChar key, size_t seed = 0) noexcept { return qHash(key.unicode(), seed); } @@ -168,9 +163,26 @@ template Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(QFlags flags, size_t seed = 0) noexcept { return qHash(flags.toInt(), seed); } -template , bool> = true> +// ### Qt 7: remove this "catch-all" overload logic, and require users +// to provide the two-argument version of qHash. +#if (QT_VERSION < QT_VERSION_CHECK(7, 0, 0)) +// Beware of moving this code from here. It needs to see all the +// declarations of qHash overloads for C++ fundamental types *before* +// its own declaration. +namespace QHashPrivate { +template +constexpr inline bool HasQHashSingleArgOverload = false; + +template +constexpr inline bool HasQHashSingleArgOverload())), size_t> +>> = true; +} + +template && !std::is_enum_v, bool> = true> size_t qHash(const T &t, size_t seed) noexcept(noexcept(qHash(t))) { return qHash(t) ^ seed; } +#endif // < Qt 7 template bool qHashEquals(const T &a, const T &b) diff --git a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp index 2d04579253..6daf418e7b 100644 --- a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp +++ b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp @@ -53,6 +53,8 @@ private Q_SLOTS: void stdPair_string_pairIntInt() { stdPair_template(QString("Hello"), std::make_pair(42, -47)); } // QTBUG-92910 void stdPair_int_pairIntPairIntInt() { stdPair_template(1, std::make_pair(2, std::make_pair(3, 4))); } + void enum_int_consistent_hash_qtbug108032(); + #if QT_DEPRECATED_SINCE(6, 6) void setGlobalQHashSeed(); #endif @@ -372,6 +374,17 @@ void tst_QHashFunctions::stdPair_template(const T1 &t1, const T2 &t2) QCOMPARE(qHash(vpair, seed), qHash(vpair, seed)); } +void tst_QHashFunctions::enum_int_consistent_hash_qtbug108032() +{ + enum E { E1, E2, E3 }; + + static_assert(QHashPrivate::HasQHashSingleArgOverload); + + QCOMPARE(qHash(E1, seed), qHash(int(E1), seed)); + QCOMPARE(qHash(E2, seed), qHash(int(E2), seed)); + QCOMPARE(qHash(E3, seed), qHash(int(E3), seed)); +} + #if QT_DEPRECATED_SINCE(6, 6) void tst_QHashFunctions::setGlobalQHashSeed() {