Q(Multi)Map: 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.

Extend the unit tests with the compile-time checks + provide the
missing tests for QMultiMap (in)equality operators.

Task-number: QTBUG-120305
Change-Id: I0a622cea29079b8437bebc873b7ba97986516eed
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Ivan Solovev 2024-07-31 15:42:44 +02:00
parent 4f2e95a089
commit f9331bc051
5 changed files with 117 additions and 16 deletions

View File

@ -5,6 +5,7 @@
#ifndef QMAP_H #ifndef QMAP_H
#define QMAP_H #define QMAP_H
#include <QtCore/qcompare.h>
#include <QtCore/qhashfunctions.h> #include <QtCore/qhashfunctions.h>
#include <QtCore/qiterator.h> #include <QtCore/qiterator.h>
#include <QtCore/qlist.h> #include <QtCore/qlist.h>
@ -243,8 +244,10 @@ public:
} }
#ifndef Q_QDOC #ifndef Q_QDOC
template <typename AKey = Key, typename AT = T> friend private:
QTypeTraits::compare_eq_result_container<QMap, AKey, AT> operator==(const QMap &lhs, const QMap &rhs) template <typename AKey = Key, typename AT = T,
QTypeTraits::compare_eq_result_container<QMap, AKey, AT> = true>
friend bool comparesEqual(const QMap &lhs, const QMap &rhs)
{ {
if (lhs.d == rhs.d) if (lhs.d == rhs.d)
return true; return true;
@ -253,13 +256,11 @@ public:
Q_ASSERT(lhs.d); Q_ASSERT(lhs.d);
return rhs.d ? (lhs.d->m == rhs.d->m) : lhs.d->m.empty(); return rhs.d ? (lhs.d->m == rhs.d->m) : lhs.d->m.empty();
} }
QT_DECLARE_EQUALITY_OPERATORS_HELPER(QMap, QMap, /* non-constexpr */, noexcept(false),
template <typename AKey = Key, typename AT = T> friend template <typename AKey = Key, typename AT = T,
QTypeTraits::compare_eq_result_container<QMap, AKey, AT> operator!=(const QMap &lhs, const QMap &rhs) QTypeTraits::compare_eq_result_container<QMap, AKey, AT> = true>)
{
return !(lhs == rhs);
}
// TODO: add the other comparison operators; std::map has them. // TODO: add the other comparison operators; std::map has them.
public:
#else #else
friend bool operator==(const QMap &lhs, const QMap &rhs); friend bool operator==(const QMap &lhs, const QMap &rhs);
friend bool operator!=(const QMap &lhs, const QMap &rhs); friend bool operator!=(const QMap &lhs, const QMap &rhs);
@ -914,8 +915,10 @@ public:
} }
#ifndef Q_QDOC #ifndef Q_QDOC
template <typename AKey = Key, typename AT = T> friend private:
QTypeTraits::compare_eq_result_container<QMultiMap, AKey, AT> operator==(const QMultiMap &lhs, const QMultiMap &rhs) template <typename AKey = Key, typename AT = T,
QTypeTraits::compare_eq_result_container<QMultiMap, AKey, AT> = true>
friend bool comparesEqual(const QMultiMap &lhs, const QMultiMap &rhs)
{ {
if (lhs.d == rhs.d) if (lhs.d == rhs.d)
return true; return true;
@ -924,13 +927,11 @@ public:
Q_ASSERT(lhs.d); Q_ASSERT(lhs.d);
return rhs.d ? (lhs.d->m == rhs.d->m) : lhs.d->m.empty(); return rhs.d ? (lhs.d->m == rhs.d->m) : lhs.d->m.empty();
} }
QT_DECLARE_EQUALITY_OPERATORS_HELPER(QMultiMap, QMultiMap, /* non-constexpr */, noexcept(false),
template <typename AKey = Key, typename AT = T> friend template <typename AKey = Key, typename AT = T,
QTypeTraits::compare_eq_result_container<QMultiMap, AKey, AT> operator!=(const QMultiMap &lhs, const QMultiMap &rhs) QTypeTraits::compare_eq_result_container<QMultiMap, AKey, AT> = true>)
{
return !(lhs == rhs);
}
// TODO: add the other comparison operators; std::multimap has them. // TODO: add the other comparison operators; std::multimap has them.
public:
#else #else
friend bool operator==(const QMultiMap &lhs, const QMultiMap &rhs); friend bool operator==(const QMultiMap &lhs, const QMultiMap &rhs);
friend bool operator!=(const QMultiMap &lhs, const QMultiMap &rhs); friend bool operator!=(const QMultiMap &lhs, const QMultiMap &rhs);

View File

@ -6,6 +6,7 @@
\class QMap \class QMap
\inmodule QtCore \inmodule QtCore
\brief The QMap class is a template class that provides an associative array. \brief The QMap class is a template class that provides an associative array.
\compares equality
\ingroup tools \ingroup tools
\ingroup shared \ingroup shared

View File

@ -6,6 +6,7 @@
\class QMultiMap \class QMultiMap
\inmodule QtCore \inmodule QtCore
\brief The QMultiMap class is a template class that provides an associative array with multiple equivalent keys. \brief The QMultiMap class is a template class that provides an associative array with multiple equivalent keys.
\compares equality
\ingroup tools \ingroup tools
\ingroup shared \ingroup shared

View File

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

View File

@ -7,6 +7,8 @@
#include <QDebug> #include <QDebug>
#include <QScopeGuard> #include <QScopeGuard>
#include <private/qcomparisontesthelper_p.h>
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
QT_WARNING_DISABLE_DEPRECATED QT_WARNING_DISABLE_DEPRECATED
@ -30,6 +32,7 @@ private slots:
void swap(); void swap();
void comparisonCompiles();
void operator_eq(); void operator_eq();
void empty(); void empty();
@ -632,6 +635,16 @@ void tst_QMap::swap()
sanityCheckTree(m2, __LINE__); sanityCheckTree(m2, __LINE__);
} }
void tst_QMap::comparisonCompiles()
{
QTestPrivate::testEqualityOperatorsCompile<QMap<int, int>>();
QTestPrivate::testEqualityOperatorsCompile<QMap<QString, QString>>();
QTestPrivate::testEqualityOperatorsCompile<QMap<QString, int>>();
QTestPrivate::testEqualityOperatorsCompile<QMultiMap<int, int>>();
QTestPrivate::testEqualityOperatorsCompile<QMultiMap<QString, QString>>();
QTestPrivate::testEqualityOperatorsCompile<QMultiMap<QString, int>>();
}
void tst_QMap::operator_eq() void tst_QMap::operator_eq()
{ {
{ {
@ -642,31 +655,37 @@ void tst_QMap::operator_eq()
QVERIFY(a == b); QVERIFY(a == b);
QCOMPARE(qHash(a), qHash(b)); QCOMPARE(qHash(a), qHash(b));
QVERIFY(!(a != b)); QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert(1,1); a.insert(1,1);
b.insert(1,1); b.insert(1,1);
QVERIFY(a == b); QVERIFY(a == b);
QCOMPARE(qHash(a), qHash(b)); QCOMPARE(qHash(a), qHash(b));
QVERIFY(!(a != b)); QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert(0,1); a.insert(0,1);
b.insert(0,1); b.insert(0,1);
QVERIFY(a == b); QVERIFY(a == b);
QCOMPARE(qHash(a), qHash(b)); QCOMPARE(qHash(a), qHash(b));
QVERIFY(!(a != b)); QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
// compare for inequality: // compare for inequality:
a.insert(42,0); a.insert(42,0);
QVERIFY(a != b); QVERIFY(a != b);
QVERIFY(!(a == b)); QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
a.insert(65, -1); a.insert(65, -1);
QVERIFY(a != b); QVERIFY(a != b);
QVERIFY(!(a == b)); QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
b.insert(-1, -1); b.insert(-1, -1);
QVERIFY(a != b); QVERIFY(a != b);
QVERIFY(!(a == b)); QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
} }
{ {
@ -677,19 +696,23 @@ void tst_QMap::operator_eq()
QVERIFY(a == b); QVERIFY(a == b);
QCOMPARE(qHash(a), qHash(b)); QCOMPARE(qHash(a), qHash(b));
QVERIFY(!(a != b)); QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert("Hello", "World"); a.insert("Hello", "World");
QVERIFY(a != b); QVERIFY(a != b);
QVERIFY(!(a == b)); QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
b.insert("Hello", "World"); b.insert("Hello", "World");
QVERIFY(a == b); QVERIFY(a == b);
QCOMPARE(qHash(a), qHash(b)); QCOMPARE(qHash(a), qHash(b));
QVERIFY(!(a != b)); QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert("Goodbye", "cruel world"); a.insert("Goodbye", "cruel world");
QVERIFY(a != b); QVERIFY(a != b);
QVERIFY(!(a == b)); QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
b.insert("Goodbye", "cruel world"); b.insert("Goodbye", "cruel world");
@ -697,12 +720,14 @@ void tst_QMap::operator_eq()
a.insert(QString(), QString()); a.insert(QString(), QString());
QVERIFY(a != b); QVERIFY(a != b);
QVERIFY(!(a == b)); QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
// empty keys and null keys match: // empty keys and null keys match:
b.insert(QString(""), QString()); b.insert(QString(""), QString());
QVERIFY(a == b); QVERIFY(a == b);
QCOMPARE(qHash(a), qHash(b)); QCOMPARE(qHash(a), qHash(b));
QVERIFY(!(a != b)); QVERIFY(!(a != b));
QT_TEST_EQUALITY_OPS(a, b, true);
} }
{ {
@ -713,6 +738,77 @@ void tst_QMap::operator_eq()
b.insert("willy", 1); b.insert("willy", 1);
QVERIFY(a != b); QVERIFY(a != b);
QVERIFY(!(a == b)); QVERIFY(!(a == b));
QT_TEST_EQUALITY_OPS(a, b, false);
}
// multimap
{
QMultiMap<int, int> a;
QMultiMap<int, int> b;
QCOMPARE_EQ(a, b);
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert(1, 1);
b.insert(1, 1);
QCOMPARE_EQ(a, b);
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert(1, 2);
QCOMPARE_NE(a, b);
QT_TEST_EQUALITY_OPS(a, b, false);
b.insert(1, 2);
QCOMPARE_EQ(a, b);
QT_TEST_EQUALITY_OPS(a, b, true);
b.insert(2, 1);
QCOMPARE_NE(a, b);
QT_TEST_EQUALITY_OPS(a, b, false);
a.insert(2, 2);
QCOMPARE_NE(a, b);
QT_TEST_EQUALITY_OPS(a, b, false);
a.insert(2, 1);
b.insert(2, 2);
// The insertion order matters!
QCOMPARE_NE(a, b);
QT_TEST_EQUALITY_OPS(a, b, false);
}
{
QMultiMap<QString, int> a;
QMultiMap<QString, int> b;
QCOMPARE_EQ(a, b);
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert("Hello", 1);
b.insert("Hello", 1);
QCOMPARE_EQ(a, b);
QT_TEST_EQUALITY_OPS(a, b, true);
a.insert("Hello", 2);
QCOMPARE_NE(a, b);
QT_TEST_EQUALITY_OPS(a, b, false);
b.insert("Hello", 2);
QCOMPARE_EQ(a, b);
QT_TEST_EQUALITY_OPS(a, b, true);
b.insert("World", 1);
QCOMPARE_NE(a, b);
QT_TEST_EQUALITY_OPS(a, b, false);
a.insert("World", 2);
QCOMPARE_NE(a, b);
QT_TEST_EQUALITY_OPS(a, b, false);
a.insert("World", 1);
b.insert("World", 2);
// The insertion order matters!
QCOMPARE_NE(a, b);
QT_TEST_EQUALITY_OPS(a, b, false);
} }
} }