Q(Multi)Hash: update (in)equality operators

Use comparison helper macros for the inequality operators.
Note that we cannot use the "public" macros, because we need to provide
attributes for the comparison of the same type, so use the _HELPER
macro.

Convert the operators to hidden friends. We do not need to use
QT_CORE_REMOVED_SINCE in this case, because the classes are templates,
so the operators were not exported.

Add the missing docs for the (in)equality operators of QMultiHash.

gcc-10 complains about accessing const_iterator::i from a hidden
friend function, claiming that it is private in that context:

 qtbase/src/corelib/tools/qhash.h:916:38: error:
  ‘QHash<QString, QVariant>::const_iterator::piter
   QHash<QString, QVariant>::const_iterator::i’ is private within
   this context
 if (i == lhs.end() || !i.i.node()->valuesEqual(it.i.node()))
                        ~~^

Use a static helper method that does this comparison.

Task-number: QTBUG-120305
Change-Id: I41b8889b659ae5dd858790a4ff629807e6666dc4
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Ivan Solovev 2024-07-31 16:41:02 +02:00
parent f9331bc051
commit b786203f08
4 changed files with 106 additions and 34 deletions

View File

@ -1510,6 +1510,7 @@ size_t qHash(long double key, size_t seed) noexcept
\class QHash
\inmodule QtCore
\brief The QHash class is a template class that provides a hash-table-based dictionary.
\compares equality
\ingroup tools
\ingroup shared
@ -1806,10 +1807,10 @@ size_t qHash(long double key, size_t seed) noexcept
fast and never fails.
*/
/*! \fn template <class Key, class T> bool QHash<Key, T>::operator==(const QHash &other) const
/*! \fn template <class Key, class T> bool QHash<Key, T>::operator==(const QHash &lhs, const QHash &rhs)
Returns \c true if \a other is equal to this hash; otherwise returns
false.
Returns \c true if \a lhs hash is equal to \a rhs hash; otherwise returns
\c false.
Two hashes are considered equal if they contain the same (key,
value) pairs.
@ -1819,9 +1820,9 @@ size_t qHash(long double key, size_t seed) noexcept
\sa operator!=()
*/
/*! \fn template <class Key, class T> bool QHash<Key, T>::operator!=(const QHash &other) const
/*! \fn template <class Key, class T> bool QHash<Key, T>::operator!=(const QHash &lhs, const QHash &rhs)
Returns \c true if \a other is not equal to this hash; otherwise
Returns \c true if \a lhs hash is not equal to \a rhs hash; otherwise
returns \c false.
Two hashes are considered equal if they contain the same (key,
@ -2901,6 +2902,7 @@ size_t qHash(long double key, size_t seed) noexcept
/*! \class QMultiHash
\inmodule QtCore
\brief The QMultiHash class is a convenience QHash subclass that provides multi-valued hashes.
\compares equality
\ingroup tools
\ingroup shared
@ -3125,6 +3127,34 @@ size_t qHash(long double key, size_t seed) noexcept
\sa insert(), value()
*/
/*!
\fn template <class Key, class T> bool QMultiHash<Key, T>::operator==(const QMultiHash &lhs, const QMultiHash &rhs)
Returns \c true if \a lhs multihash equals to the \a rhs multihash;
otherwise returns \c false.
Two multihashes are considered equal if they contain the same (key, value)
pairs.
This function requires the value type to implement \c {operator==()}.
\sa operator!=()
*/
/*!
\fn template <class Key, class T> bool QMultiHash<Key, T>::operator!=(const QMultiHash &lhs, const QMultiHash &rhs)
Returns \c true if \a lhs multihash is not equal to the \a rhs multihash;
otherwise returns \c false.
Two multihashes are considered equal if they contain the same (key, value)
pairs.
This function requires the value type to implement \c {operator==()}.
\sa operator==()
*/
/*! \fn template <class Key, class T> QMultiHash &QMultiHash<Key, T>::operator+=(const QMultiHash &other)
Inserts all the items in the \a other hash into this hash

View File

@ -900,29 +900,39 @@ public:
#endif
void swap(QHash &other) noexcept { qt_ptr_swap(d, other.d); }
class const_iterator;
#ifndef Q_QDOC
template <typename AKey = Key, typename AT = T>
QTypeTraits::compare_eq_result_container<QHash, AKey, AT> operator==(const QHash &other) const noexcept
private:
static bool compareIterators(const const_iterator &lhs, const const_iterator &rhs)
{
if (d == other.d)
return lhs.i.node()->valuesEqual(rhs.i.node());
}
template <typename AKey = Key, typename AT = T,
QTypeTraits::compare_eq_result_container<QHash, AKey, AT> = true>
friend bool comparesEqual(const QHash &lhs, const QHash &rhs) noexcept
{
if (lhs.d == rhs.d)
return true;
if (size() != other.size())
if (lhs.size() != rhs.size())
return false;
for (const_iterator it = other.begin(); it != other.end(); ++it) {
const_iterator i = find(it.key());
if (i == end() || !i.i.node()->valuesEqual(it.i.node()))
for (const_iterator it = rhs.begin(); it != rhs.end(); ++it) {
const_iterator i = lhs.find(it.key());
if (i == lhs.end() || !compareIterators(i, it))
return false;
}
// all values must be the same as size is the same
return true;
}
template <typename AKey = Key, typename AT = T>
QTypeTraits::compare_eq_result_container<QHash, AKey, AT> operator!=(const QHash &other) const noexcept
{ return !(*this == other); }
QT_DECLARE_EQUALITY_OPERATORS_HELPER(QHash, QHash, /* non-constexpr */, noexcept,
template <typename AKey = Key, typename AT = T,
QTypeTraits::compare_eq_result_container<QHash, AKey, AT> = true>)
public:
#else
bool operator==(const QHash &other) const;
bool operator!=(const QHash &other) const;
friend bool operator==(const QHash &lhs, const QHash &rhs) noexcept;
friend bool operator!=(const QHash &lhs, const QHash &rhs) noexcept;
#endif // Q_QDOC
inline qsizetype size() const noexcept { return d ? qsizetype(d->size) : 0; }
@ -1098,8 +1108,6 @@ public:
}
QList<T> values() const { return QList<T>(begin(), end()); }
class const_iterator;
class iterator
{
using piter = typename QHashPrivate::iterator<Node>;
@ -1523,22 +1531,24 @@ public:
}
#ifndef Q_QDOC
template <typename AKey = Key, typename AT = T>
QTypeTraits::compare_eq_result_container<QMultiHash, AKey, AT> operator==(const QMultiHash &other) const noexcept
private:
template <typename AKey = Key, typename AT = T,
QTypeTraits::compare_eq_result_container<QMultiHash, AKey, AT> = true>
friend bool comparesEqual(const QMultiHash &lhs, const QMultiHash &rhs) noexcept
{
if (d == other.d)
if (lhs.d == rhs.d)
return true;
if (m_size != other.m_size)
if (lhs.m_size != rhs.m_size)
return false;
if (m_size == 0)
if (lhs.m_size == 0)
return true;
// equal size, and both non-zero size => d pointers allocated for both
Q_ASSERT(d);
Q_ASSERT(other.d);
if (d->size != other.d->size)
Q_ASSERT(lhs.d);
Q_ASSERT(rhs.d);
if (lhs.d->size != rhs.d->size)
return false;
for (auto it = other.d->begin(); it != other.d->end(); ++it) {
auto *n = d->findNode(it.node()->key);
for (auto it = rhs.d->begin(); it != rhs.d->end(); ++it) {
auto *n = lhs.d->findNode(it.node()->key);
if (!n)
return false;
Chain *e = it.node()->value;
@ -1557,12 +1567,13 @@ public:
// all values must be the same as size is the same
return true;
}
template <typename AKey = Key, typename AT = T>
QTypeTraits::compare_eq_result_container<QMultiHash, AKey, AT> operator!=(const QMultiHash &other) const noexcept
{ return !(*this == other); }
QT_DECLARE_EQUALITY_OPERATORS_HELPER(QMultiHash, QMultiHash, /* non-constexpr */, noexcept,
template <typename AKey = Key, typename AT = T,
QTypeTraits::compare_eq_result_container<QMultiHash, AKey, AT> = true>)
public:
#else
bool operator==(const QMultiHash &other) const;
bool operator!=(const QMultiHash &other) const;
friend bool operator==(const QMultiHash &lhs, const QMultiHash &rhs) noexcept;
friend bool operator!=(const QMultiHash &lhs, const QMultiHash &rhs) noexcept;
#endif // Q_QDOC
inline qsizetype size() const noexcept { return m_size; }

View File

@ -14,6 +14,8 @@ endif()
qt_internal_add_test(tst_qhash
SOURCES
tst_qhash.cpp
LIBRARIES
Qt::TestPrivate
)
qt_internal_undefine_global_definition(tst_qhash QT_NO_JAVA_STYLE_ITERATORS)

View File

@ -16,6 +16,8 @@
#include <qsemaphore.h>
#include <private/qcomparisontesthelper_p.h>
using namespace Qt::StringLiterals;
class tst_QHash : public QObject
@ -37,6 +39,7 @@ private slots:
void contains(); // copied from tst_QMap
void qhash();
void take(); // copied from tst_QMap
void comparisonCompiles();
void operator_eq(); // slightly modified from tst_QMap
void heterogeneousSearch();
void heterogeneousSearchConstKey();
@ -1025,6 +1028,16 @@ void tst_QHash::take()
}
}
void tst_QHash::comparisonCompiles()
{
QTestPrivate::testEqualityOperatorsCompile<QHash<int, int>>();
QTestPrivate::testEqualityOperatorsCompile<QHash<QString, QString>>();
QTestPrivate::testEqualityOperatorsCompile<QHash<QString, int>>();
QTestPrivate::testEqualityOperatorsCompile<QMultiHash<int, int>>();
QTestPrivate::testEqualityOperatorsCompile<QMultiHash<QString, QString>>();
QTestPrivate::testEqualityOperatorsCompile<QMultiHash<QString, int>>();
}
// slightly modified from tst_QMap
void tst_QHash::operator_eq()
{
@ -1035,29 +1048,35 @@ void tst_QHash::operator_eq()
QVERIFY(a == b);
QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert(1,1);
b.insert(1,1);
QVERIFY(a == b);
QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert(0,1);
b.insert(0,1);
QVERIFY(a == b);
QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
// compare for inequality:
a.insert(42,0);
QVERIFY(a != b);
QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
a.insert(65, -1);
QVERIFY(a != b);
QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
b.insert(-1, -1);
QVERIFY(a != b);
QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
}
{
@ -1067,18 +1086,22 @@ void tst_QHash::operator_eq()
QVERIFY(a == b);
QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert("Hello", "World");
QVERIFY(a != b);
QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
b.insert("Hello", "World");
QVERIFY(a == b);
QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert("Goodbye", "cruel world");
QVERIFY(a != b);
QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
b.insert("Goodbye", "cruel world");
@ -1086,11 +1109,13 @@ void tst_QHash::operator_eq()
a.insert(QString(), QString());
QVERIFY(a != b);
QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
// empty keys and null keys match:
b.insert(QString(""), QString());
QVERIFY(a == b);
QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
}
{
@ -1101,6 +1126,7 @@ void tst_QHash::operator_eq()
b.insert("willy", 1);
QVERIFY(a != b);
QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
}
// unlike multi-maps, multi-hashes should be equal iff their contents are equal,
@ -1118,6 +1144,7 @@ void tst_QHash::operator_eq()
QVERIFY(a == b);
QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
}
{
@ -1138,6 +1165,7 @@ void tst_QHash::operator_eq()
QVERIFY(a == b);
QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
}
{
@ -1166,6 +1194,7 @@ void tst_QHash::operator_eq()
QVERIFY(a == b);
QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
}
}