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 /src/corelib/tools/qhash.h | |
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 'src/corelib/tools/qhash.h')
-rw-r--r-- | src/corelib/tools/qhash.h | 46 |
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) { |