QHash: add the ability to detect whether qHash(t) == qHash(K(t))

The typical examples are views and their corresponding owning
container. Since they are different types, the search is heterogeneous,
hence the name.

We have to do it differently from the Standard Library. There, because
the hasher is a template parameter to std::unordered_{map,set}, it can
be a structure with overloads for different types, all of which the
implementer guarantees produce the same hash for input that also
compares equal. For QHash/QSet, we don't have a template parameter.

One alternative solution would be to detect the existence of
qHashEquals(T1, T2) or qHashHeterogeneousEquals() or something.

Change-Id: I664b9f014ffc48cbb49bfffd17b0318c0775a2b5
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
bb10
Thiago Macieira 2024-02-02 16:05:36 -08:00
parent 07c8cece05
commit 52abff8cc1
1 changed files with 36 additions and 0 deletions

View File

@ -13,6 +13,10 @@
#include <functional> // for std::hash
#include <utility> // For std::pair
#ifdef __cpp_concepts
# include <concepts>
#endif
#if 0
#pragma qt_class(QHashFunctions)
#endif
@ -46,6 +50,15 @@ private:
size_t data;
};
// Whether, ∀ t of type T && ∀ seed, qHash(Key(t), seed) == qHash(t, seed)
template <typename Key, typename T> struct QHashHeterogeneousSearch : std::false_type {};
// Specializations
template <> struct QHashHeterogeneousSearch<QString, QStringView> : std::true_type {};
template <> struct QHashHeterogeneousSearch<QStringView, QString> : std::true_type {};
template <> struct QHashHeterogeneousSearch<QByteArray, QByteArrayView> : std::true_type {};
template <> struct QHashHeterogeneousSearch<QByteArrayView, QByteArray> : std::true_type {};
namespace QHashPrivate {
Q_DECL_CONST_FUNCTION constexpr size_t hash(size_t key, size_t seed) noexcept
@ -217,12 +230,35 @@ size_t qHash(const T &t, size_t seed) noexcept(noexcept(qHash(t)))
{ return qHash(t) ^ seed; }
#endif // < Qt 7
namespace QHashPrivate {
#ifdef __cpp_concepts
template <typename Key, typename T> concept HeterogeneouslySearchableWithHelper =
// if Key and T are not the same (member already exists)
!std::is_same_v<Key, T>
// but are comparable amongst each other
&& std::equality_comparable_with<Key, T>
// and supports heteregenous hashing
&& QHashHeterogeneousSearch<Key, T>::value;
template <typename Key, typename T> concept HeterogeneouslySearchableWith =
HeterogeneouslySearchableWithHelper<q20::remove_cvref_t<Key>, q20::remove_cvref_t<T>>;
#else
template <typename Key, typename T> constexpr bool HeterogeneouslySearchableWith = false;
#endif
}
template<typename T>
bool qHashEquals(const T &a, const T &b)
{
return a == b;
}
template <typename T1, typename T2>
std::enable_if_t<QHashPrivate::HeterogeneouslySearchableWith<T1, T2>, bool>
qHashEquals(const T1 &a, const T2 &b)
{
return a == b;
}
namespace QtPrivate {
struct QHashCombine