QHash: Unconceptify heterogeneous search code
That allows the code to run in C++17 mode - at the expense of significantly worse compile time error messages, potentially worse compilation speed and harder to read code. Adjust the test type to actually model detail::is_equality_comparable_with (which requires all four relational operators even in C++17). As a drive-by, fix the return value of remove() from auto to bool. Done-with: Marc Mutz <marc.mutz@qt.io> Fixes: QTBUG-128470 Change-Id: I68df26db579c60812a18e09b76dd5712e73ccaa2 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> (cherry picked from commit 7fe3cee36352c74cbaaff52e863bd0da1170affa) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
16ed1fe089
commit
722c7edf03
@ -1377,69 +1377,83 @@ private:
|
||||
return iterator(result.it);
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
using if_heterogeneously_seachable = QHashPrivate::if_heterogeneously_seachable_with<Key, K>;
|
||||
|
||||
public:
|
||||
#ifdef __cpp_concepts
|
||||
bool remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
bool remove(const K &key)
|
||||
{
|
||||
return removeImpl(key);
|
||||
}
|
||||
T take(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
T take(const K &key)
|
||||
{
|
||||
return takeImpl(key);
|
||||
}
|
||||
bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
bool contains(const K &key) const
|
||||
{
|
||||
return d ? d->findNode(key) != nullptr : false;
|
||||
}
|
||||
qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
qsizetype count(const K &key) const
|
||||
{
|
||||
return contains(key) ? 1 : 0;
|
||||
}
|
||||
T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
T value(const K &key) const noexcept
|
||||
{
|
||||
if (auto *v = valueImpl(key))
|
||||
return *v;
|
||||
else
|
||||
return T();
|
||||
}
|
||||
T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &defaultValue) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
T value(const K &key, const T &defaultValue) const noexcept
|
||||
{
|
||||
if (auto *v = valueImpl(key))
|
||||
return *v;
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
T &operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
T &operator[](const K &key)
|
||||
{
|
||||
return operatorIndexImpl(key);
|
||||
}
|
||||
const T operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
const T operator[](const K &key) const noexcept
|
||||
{
|
||||
return value(key);
|
||||
}
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
std::pair<iterator, iterator>
|
||||
equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
equal_range(const K &key)
|
||||
{
|
||||
return equal_range_impl(*this, key);
|
||||
}
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
std::pair<const_iterator, const_iterator>
|
||||
equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
equal_range(const K &key) const noexcept
|
||||
{
|
||||
return equal_range_impl(*this, key);
|
||||
}
|
||||
iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
iterator find(const K &key)
|
||||
{
|
||||
return findImpl(key);
|
||||
}
|
||||
const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
const_iterator find(const K &key) const noexcept
|
||||
{
|
||||
return constFindImpl(key);
|
||||
}
|
||||
const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
const_iterator constFind(const K &key) const noexcept
|
||||
{
|
||||
return find(key);
|
||||
}
|
||||
#endif // __cpp_concepts
|
||||
};
|
||||
|
||||
|
||||
@ -2372,99 +2386,120 @@ private:
|
||||
return iterator(result.it);
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
using if_heterogeneously_seachable = QHashPrivate::if_heterogeneously_seachable_with<Key, K>;
|
||||
|
||||
public:
|
||||
#ifdef __cpp_concepts
|
||||
qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
qsizetype remove(const K &key)
|
||||
{
|
||||
return removeImpl(key);
|
||||
}
|
||||
T take(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
T take(const K &key)
|
||||
{
|
||||
return takeImpl(key);
|
||||
}
|
||||
bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
bool contains(const K &key) const noexcept
|
||||
{
|
||||
if (!d)
|
||||
return false;
|
||||
return d->findNode(key) != nullptr;
|
||||
}
|
||||
T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
T value(const K &key) const noexcept
|
||||
{
|
||||
if (auto *v = valueImpl(key))
|
||||
return *v;
|
||||
else
|
||||
return T();
|
||||
}
|
||||
T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &defaultValue) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
T value(const K &key, const T &defaultValue) const noexcept
|
||||
{
|
||||
if (auto *v = valueImpl(key))
|
||||
return *v;
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
T &operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
T &operator[](const K &key)
|
||||
{
|
||||
return operatorIndexImpl(key);
|
||||
}
|
||||
const T operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
const T operator[](const K &key) const noexcept
|
||||
{
|
||||
return value(key);
|
||||
}
|
||||
QList<T> values(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
QList<T> values(const K &key)
|
||||
{
|
||||
return valuesImpl(key);
|
||||
}
|
||||
iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
iterator find(const K &key)
|
||||
{
|
||||
return findImpl(key);
|
||||
}
|
||||
const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
const_iterator constFind(const K &key) const noexcept
|
||||
{
|
||||
return constFindImpl(key);
|
||||
}
|
||||
const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
const_iterator find(const K &key) const noexcept
|
||||
{
|
||||
return constFindImpl(key);
|
||||
}
|
||||
bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
bool contains(const K &key, const T &value) const noexcept
|
||||
{
|
||||
return containsImpl(key, value);
|
||||
}
|
||||
qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value)
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
qsizetype remove(const K &key, const T &value)
|
||||
{
|
||||
return removeImpl(key, value);
|
||||
}
|
||||
qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
qsizetype count(const K &key) const noexcept
|
||||
{
|
||||
return countImpl(key);
|
||||
}
|
||||
qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
qsizetype count(const K &key, const T &value) const noexcept
|
||||
{
|
||||
return countImpl(key, value);
|
||||
}
|
||||
iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value)
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
iterator find(const K &key, const T &value)
|
||||
{
|
||||
return findImpl(key, value);
|
||||
}
|
||||
const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
const_iterator constFind(const K &key, const T &value) const noexcept
|
||||
{
|
||||
return constFindImpl(key, value);
|
||||
}
|
||||
const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
const_iterator find(const K &key, const T &value) const noexcept
|
||||
{
|
||||
return constFind(key, value);
|
||||
}
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
std::pair<iterator, iterator>
|
||||
equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
equal_range(const K &key)
|
||||
{
|
||||
return equal_range_impl(key);
|
||||
}
|
||||
template <typename K, if_heterogeneously_seachable<K> = true>
|
||||
std::pair<const_iterator, const_iterator>
|
||||
equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
equal_range(const K &key) const noexcept
|
||||
{
|
||||
return equal_range_impl(key);
|
||||
}
|
||||
#endif // __cpp_concepts
|
||||
};
|
||||
|
||||
Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(Hash)
|
||||
|
@ -9,9 +9,6 @@
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qstringfwd.h>
|
||||
|
||||
#ifdef __cpp_concepts
|
||||
#include <concepts>
|
||||
#endif
|
||||
#include <numeric> // for std::accumulate
|
||||
#include <functional> // for std::hash
|
||||
#include <utility> // For std::pair
|
||||
@ -238,19 +235,44 @@ size_t qHash(const T &t, size_t seed, Args&&...) noexcept(noexcept(qHash(t)))
|
||||
#endif // < Qt 7
|
||||
|
||||
namespace QHashPrivate {
|
||||
#ifdef __cpp_concepts
|
||||
template <typename Key, typename T> concept HeterogeneouslySearchableWithHelper =
|
||||
|
||||
namespace detail {
|
||||
// approximates std::equality_comparable_with
|
||||
template <typename T, typename U, typename = void>
|
||||
struct is_equality_comparable_with : std::false_type {};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_equality_comparable_with<T, U,
|
||||
std::void_t<
|
||||
decltype(bool(std::declval<T>() == std::declval<U>())),
|
||||
decltype(bool(std::declval<U>() == std::declval<T>())),
|
||||
decltype(bool(std::declval<T>() != std::declval<U>())),
|
||||
decltype(bool(std::declval<U>() != std::declval<T>()))
|
||||
>>
|
||||
: std::true_type {};
|
||||
}
|
||||
|
||||
template <typename Key, typename T> struct HeterogeneouslySearchableWithHelper
|
||||
: std::conjunction<
|
||||
// if Key and T are not the same (member already exists)
|
||||
!std::is_same_v<Key, T>
|
||||
std::negation<std::is_same<Key, T>>,
|
||||
// but are comparable amongst each other
|
||||
&& std::equality_comparable_with<Key, T>
|
||||
detail::is_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
|
||||
QHashHeterogeneousSearch<Key, T>
|
||||
> {};
|
||||
|
||||
template <typename Key, typename T>
|
||||
using HeterogeneouslySearchableWith = HeterogeneouslySearchableWithHelper<
|
||||
q20::remove_cvref_t<Key>,
|
||||
q20::remove_cvref_t<T>
|
||||
>;
|
||||
|
||||
template <typename Key, typename K>
|
||||
using if_heterogeneously_seachable_with = std::enable_if_t<
|
||||
QHashPrivate::HeterogeneouslySearchableWith<Key, K>::value,
|
||||
bool>;
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -259,9 +281,8 @@ 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)
|
||||
template <typename T1, typename T2, QHashPrivate::if_heterogeneously_seachable_with<T1, T2> = true>
|
||||
bool qHashEquals(const T1 &a, const T2 &b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
@ -1169,9 +1169,12 @@ void tst_QHash::operator_eq()
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cpp_concepts
|
||||
struct HeterogeneousHashingType
|
||||
{
|
||||
#ifndef __cpp_aggregate_paren_init
|
||||
HeterogeneousHashingType() = default;
|
||||
HeterogeneousHashingType(const QString &string) : s(string) {}
|
||||
#endif
|
||||
inline static int conversionCount = 0;
|
||||
QString s;
|
||||
|
||||
@ -1182,12 +1185,18 @@ struct HeterogeneousHashingType
|
||||
}
|
||||
|
||||
// std::equality_comparable_with requires we be self-comparable too
|
||||
friend bool operator==(const HeterogeneousHashingType &t1, const HeterogeneousHashingType &t2) = default;
|
||||
friend bool operator==(const HeterogeneousHashingType &t1, const HeterogeneousHashingType &t2) { return t1.s == t2.s; };
|
||||
|
||||
friend bool operator==(const QString &string, const HeterogeneousHashingType &tester)
|
||||
{ return tester.s == string; }
|
||||
#ifndef __cpp_impl_three_way_compare // full set required for detail::is_equality_comparable_with<QString>
|
||||
friend bool operator!=(const QString &string, const HeterogeneousHashingType &tester)
|
||||
{ return !(tester.s == string); }
|
||||
{ return !operator==(string, tester); }
|
||||
friend bool operator==(const HeterogeneousHashingType &tester, const QString &string)
|
||||
{ return operator==(string, tester); }
|
||||
friend bool operator!=(const HeterogeneousHashingType &tester, const QString &string)
|
||||
{ return !operator==(string, tester); }
|
||||
#endif
|
||||
|
||||
friend size_t qHash(const HeterogeneousHashingType &tester, size_t seed)
|
||||
{ return qHash(tester.s, seed); }
|
||||
@ -1196,10 +1205,9 @@ QT_BEGIN_NAMESPACE
|
||||
template <> struct QHashHeterogeneousSearch<QString, HeterogeneousHashingType> : std::true_type {};
|
||||
template <> struct QHashHeterogeneousSearch<HeterogeneousHashingType, QString> : std::true_type {};
|
||||
QT_END_NAMESPACE
|
||||
static_assert(std::is_same_v<QString, std::common_type_t<QString, HeterogeneousHashingType>>);
|
||||
static_assert(std::equality_comparable_with<QString, HeterogeneousHashingType>);
|
||||
static_assert(QHashPrivate::HeterogeneouslySearchableWith<QString, HeterogeneousHashingType>);
|
||||
static_assert(QHashPrivate::HeterogeneouslySearchableWith<HeterogeneousHashingType, QString>);
|
||||
static_assert(QHashPrivate::detail::is_equality_comparable_with<QString, HeterogeneousHashingType>::value);
|
||||
static_assert(QHashPrivate::HeterogeneouslySearchableWith<QString, HeterogeneousHashingType>::value);
|
||||
static_assert(QHashPrivate::HeterogeneouslySearchableWith<HeterogeneousHashingType, QString>::value);
|
||||
|
||||
template <typename T> struct HeterogeneousSearchTestHelper
|
||||
{
|
||||
@ -1219,14 +1227,10 @@ template <> struct HeterogeneousSearchTestHelper<HeterogeneousHashingType>
|
||||
QCOMPARE(HeterogeneousHashingType::conversionCount, 0);
|
||||
}
|
||||
};
|
||||
#else
|
||||
using HeterogeneousHashingType = QString;
|
||||
#endif
|
||||
|
||||
template <template <typename, typename> class Hash, typename String, typename View, typename Converter>
|
||||
static void heterogeneousSearchTest(const QList<std::remove_const_t<String>> &keys, Converter conv)
|
||||
{
|
||||
#ifdef __cpp_concepts
|
||||
using Helper = HeterogeneousSearchTestHelper<View>;
|
||||
String key = keys.last();
|
||||
String otherKey = keys.first();
|
||||
@ -1334,11 +1338,6 @@ static void heterogeneousSearchTest(const QList<std::remove_const_t<String>> &ke
|
||||
QCOMPARE_EQ(hash.find(keyView), hash.end());
|
||||
QCOMPARE_EQ(hash.constFind(keyView), hash.constEnd());
|
||||
Helper::checkCounter();
|
||||
#else
|
||||
Q_UNUSED(keys);
|
||||
Q_UNUSED(conv);
|
||||
QSKIP("This feature requires C++20 (concepts)");
|
||||
#endif
|
||||
}
|
||||
|
||||
template <template <typename, typename> class Hash, typename String, typename View>
|
||||
|
Loading…
x
Reference in New Issue
Block a user