summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qhash.h
diff options
context:
space:
mode:
authorGiuseppe D'Angelo <giuseppe.dangelo@kdab.com>2020-11-27 17:39:10 +0100
committerGiuseppe D'Angelo <giuseppe.dangelo@kdab.com>2020-11-30 17:16:22 +0100
commit81e893fb2d57b7f2d332f4f626e38f51acf43542 (patch)
treeb8bc1fc0a30a6419f75094433cdf52025e5f0da0 /src/corelib/tools/qhash.h
parent5dab710b90e7041aaa9fcf3631f2ca6af1baab5f (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 'src/corelib/tools/qhash.h')
-rw-r--r--src/corelib/tools/qhash.h46
1 files changed, 44 insertions, 2 deletions
diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h
index 5dc88943fc..8134f4402c 100644
--- a/src/corelib/tools/qhash.h
+++ b/src/corelib/tools/qhash.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -48,6 +49,7 @@
#include <QtCore/qrefcount.h>
#include <initializer_list>
+#include <functional> // for std::hash
QT_BEGIN_NAMESPACE
@@ -58,6 +60,46 @@ struct QHashDummyValue
namespace QHashPrivate {
+template <typename T, typename = void>
+constexpr inline bool HasQHashOverload = false;
+
+template <typename T>
+constexpr inline bool HasQHashOverload<T, std::enable_if_t<
+ std::is_convertible_v<decltype(qHash(std::declval<const T &>(), std::declval<size_t>())), size_t>
+>> = true;
+
+template <typename T, typename = void>
+constexpr inline bool HasStdHashSpecializationWithSeed = false;
+
+template <typename T>
+constexpr inline bool HasStdHashSpecializationWithSeed<T, std::enable_if_t<
+ std::is_convertible_v<decltype(std::hash<T>()(std::declval<const T &>(), std::declval<size_t>())), size_t>
+>> = true;
+
+template <typename T, typename = void>
+constexpr inline bool HasStdHashSpecializationWithoutSeed = false;
+
+template <typename T>
+constexpr inline bool HasStdHashSpecializationWithoutSeed<T, std::enable_if_t<
+ std::is_convertible_v<decltype(std::hash<T>()(std::declval<const T &>())), size_t>
+>> = true;
+
+template <typename T>
+size_t calculateHash(const T &t, size_t seed)
+{
+ if constexpr (HasQHashOverload<T>) {
+ return qHash(t, seed);
+ } else if constexpr (HasStdHashSpecializationWithSeed<T>) {
+ return std::hash<T>()(t, seed);
+ } else if constexpr (HasStdHashSpecializationWithoutSeed<T>) {
+ Q_UNUSED(seed);
+ return std::hash<T>()(t);
+ } else {
+ static_assert(sizeof(T) == 0, "The key type must have a qHash overload or a std::hash specialization");
+ return 0;
+ }
+}
+
// QHash uses a power of two growth policy.
namespace GrowthPolicy {
inline constexpr size_t maxNumBuckets() noexcept
@@ -545,7 +587,7 @@ struct Data
iterator find(const Key &key) const noexcept
{
Q_ASSERT(numBuckets > 0);
- size_t hash = qHash(key, seed);
+ size_t hash = QHashPrivate::calculateHash(key, seed);
size_t bucket = GrowthPolicy::bucketForHash(numBuckets, hash);
// loop over the buckets until we find the entry we search for
// or an empty slot, in which case we know the entry doesn't exist
@@ -614,7 +656,7 @@ struct Data
size_t nextIndex = next & Span::LocalBucketMask;
if (!spans[nextSpan].hasNode(nextIndex))
break;
- size_t hash = qHash(spans[nextSpan].at(nextIndex).key, seed);
+ size_t hash = QHashPrivate::calculateHash(spans[nextSpan].at(nextIndex).key, seed);
size_t newBucket = GrowthPolicy::bucketForHash(numBuckets, hash);
while (true) {
if (newBucket == next) {