diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2020-11-27 17:39:10 +0100 |
---|---|---|
committer | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2020-11-30 17:16:22 +0100 |
commit | 81e893fb2d57b7f2d332f4f626e38f51acf43542 (patch) | |
tree | b8bc1fc0a30a6419f75094433cdf52025e5f0da0 /tests/auto/corelib/tools/qhash/tst_qhash.cpp | |
parent | 5dab710b90e7041aaa9fcf3631f2ca6af1baab5f (diff) |
QHash: support std::hash as hashing function
In addition (and as a fallback) from requiring qHash, add support
for std::hash specializations. This catches two birds with one stone:
1) users of Qt can simply specialize std::hash for their datatypes,
and use them in both QHash and stdlib unordered associative containers;
2) we get QHash support for any (stdlib) datatype that is hashable
without having to overload qHash for them.
[ChangeLog][QtCore][QHash] QHash, QMultiHash and QSet now support
for key types anything that can be hashed via std::hash, instead of
always requiring a qHash() overload.
Change-Id: Ib5ecba86e4b376d318389500bd24883ac6534c5f
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Diffstat (limited to 'tests/auto/corelib/tools/qhash/tst_qhash.cpp')
-rw-r--r-- | tests/auto/corelib/tools/qhash/tst_qhash.cpp | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index f61dfda720..c6e26f24c0 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -33,6 +33,8 @@ #include <algorithm> #include <vector> +#include <unordered_set> +#include <string> class tst_QHash : public QObject { @@ -76,6 +78,8 @@ private slots: void badHashFunction(); void hashOfHash(); + + void stdHash(); }; struct IdentityTracker { @@ -1883,5 +1887,87 @@ void tst_QHash::hashOfHash() (void)qHash(multiHash); } +template <bool HasQHash_> +struct StdHashKeyType { + static inline constexpr bool HasQHash = HasQHash_; + static bool StdHashUsed; + + int i; + friend bool operator==(const StdHashKeyType &lhs, const StdHashKeyType &rhs) + { return lhs.i == rhs.i; } +}; + +template <bool HasQHash> +bool StdHashKeyType<HasQHash>::StdHashUsed = false; + +namespace std { +template <bool HasQHash> struct hash<StdHashKeyType<HasQHash>> +{ + size_t operator()(const StdHashKeyType<HasQHash> &s, size_t seed = 0) const { + StdHashKeyType<HasQHash>::StdHashUsed = true; + return hash<int>()(s.i) ^ seed; + } +}; +} + +template <bool HasQHash> +std::enable_if_t<HasQHash, size_t> +qHash(const StdHashKeyType<HasQHash> &s, size_t seed) +{ + return qHash(s.i, seed); +} + +template <typename T> +void stdHashImpl() +{ + QHash<T, int> hash; + for (int i = 0; i < 1000; ++i) + hash.insert(T{i}, i); + + QCOMPARE(hash.size(), 1000); + for (int i = 0; i < 1000; ++i) + QCOMPARE(hash.value(T{i}, -1), i); + + for (int i = 500; i < 1500; ++i) + hash.insert(T{i}, i); + + QCOMPARE(hash.size(), 1500); + for (int i = 0; i < 1500; ++i) + QCOMPARE(hash.value(T{i}, -1), i); + + qsizetype count = 0; + for (int i = -2000; i < 2000; ++i) { + if (hash.contains(T{i})) + ++count; + } + QCOMPARE(count, 1500); + QCOMPARE(T::StdHashUsed, !T::HasQHash); + + + std::unordered_set<T> set; + for (int i = 0; i < 1000; ++i) + set.insert(T{i}); + + for (int i = 500; i < 1500; ++i) + set.insert(T{i}); + + QCOMPARE(set.size(), size_t(1500)); + count = 0; + for (int i = -2000; i < 2000; ++i) + count += qsizetype(set.count(T{i})); + QCOMPARE(count, 1500); + QVERIFY(T::StdHashUsed); +} + +void tst_QHash::stdHash() +{ + stdHashImpl<StdHashKeyType<false>>(); + stdHashImpl<StdHashKeyType<true>>(); + + QSet<std::string> strings{ "a", "b", "c" }; + QVERIFY(strings.contains("a")); + QVERIFY(!strings.contains("z")); +} + QTEST_APPLESS_MAIN(tst_QHash) #include "tst_qhash.moc" |