diff --git a/src/corelib/global/qcomparehelpers.h b/src/corelib/global/qcomparehelpers.h index 97c972bfa78..18bc1b5e2e4 100644 --- a/src/corelib/global/qcomparehelpers.h +++ b/src/corelib/global/qcomparehelpers.h @@ -559,9 +559,103 @@ constexpr Qt::strong_ordering compareThreeWay(Enum lhs, Enum rhs) noexcept { return compareThreeWay(qToUnderlying(lhs), qToUnderlying(rhs)); } - } // namespace Qt +namespace QtOrderingPrivate { + +template +constexpr std::tuple qt_tuple_pop_front_impl(const std::tuple &t, + std::index_sequence) noexcept +{ + return std::tuple(std::get(t)...); +} + +template +constexpr std::tuple qt_tuple_pop_front(const std::tuple &t) noexcept +{ + return qt_tuple_pop_front_impl(t, std::index_sequence_for{}); +} + +template +constexpr auto compareThreeWayMulti(const std::tuple &lhs, // ie. not empty + const std::tuple &rhs) noexcept +{ + static_assert(sizeof...(LhsTail) == sizeof...(RhsTail), + // expanded together below, but provide a nicer error message: + "The tuple arguments have to have the same size."); + + using Qt::compareThreeWay; + using R = std::common_type_t< + decltype(compareThreeWay(std::declval(), std::declval())), + decltype(compareThreeWay(std::declval(), std::declval()))... + >; + + const auto &l = std::get<0>(lhs); + const auto &r = std::get<0>(rhs); + static_assert(noexcept(compareThreeWay(l, r)), + "This function requires all relational operators to be noexcept."); + const auto res = compareThreeWay(l, r); + if constexpr (sizeof...(LhsTail) > 0) { + if (is_eq(res)) + return R{compareThreeWayMulti(qt_tuple_pop_front(lhs), qt_tuple_pop_front(rhs))}; + } + return R{res}; +} + +} //QtOrderingPrivate + +namespace Qt { +// A wrapper class that adapts the wrappee to use the strongly-ordered +// function objects for implementing the relational operators. +// Mostly useful to avoid UB on pointers (which it currently mandates P to be), +// because all the comparison helpers (incl. std::compare_three_way on +// std::tuple!) will use the language-level operators. +// +template +class totally_ordered_wrapper +{ + static_assert(std::is_pointer_v

); + using T = std::remove_pointer_t

; + + P ptr; +public: + explicit constexpr totally_ordered_wrapper(P p) : ptr(p) {} + + constexpr P get() const noexcept { return ptr; } + constexpr P operator->() const noexcept { return get(); } + constexpr T& operator*() const noexcept { return *get(); } + + explicit constexpr operator bool() const noexcept { return get(); } + +private: + friend constexpr auto compareThreeWay(const totally_ordered_wrapper &lhs, const totally_ordered_wrapper &rhs) noexcept + { return Qt::compareThreeWay(lhs.ptr, rhs.ptr); } +#define MAKE_RELOP(Ret, op, Op) \ + friend constexpr Ret operator op (const totally_ordered_wrapper &lhs, const totally_ordered_wrapper &rhs) noexcept \ + { return std:: Op {}(lhs.ptr, rhs.ptr); } \ + friend constexpr Ret operator op (const totally_ordered_wrapper &lhs, const P &rhs) noexcept \ + { return std:: Op {}(lhs.ptr, rhs ); } \ + friend constexpr Ret operator op (const P &lhs, const totally_ordered_wrapper &rhs) noexcept \ + { return std:: Op {}(lhs, rhs.ptr); } \ + friend constexpr Ret operator op (const totally_ordered_wrapper &lhs, std::nullptr_t) noexcept \ + { return std:: Op {}(lhs.ptr, nullptr); } \ + friend constexpr Ret operator op (std::nullptr_t, const totally_ordered_wrapper &rhs) noexcept \ + { return std:: Op {}(nullptr, rhs.ptr); } \ + /* end */ + MAKE_RELOP(bool, ==, equal_to

) + MAKE_RELOP(bool, !=, not_equal_to

) + MAKE_RELOP(bool, < , less

) + MAKE_RELOP(bool, <=, less_equal

) + MAKE_RELOP(bool, > , greater

) + MAKE_RELOP(bool, >=, greater_equal

) +#ifdef __cpp_lib_three_way_comparison + MAKE_RELOP(auto, <=>, compare_three_way) +#endif +#undef MAKE_RELOP +}; + +} //Qt + QT_END_NAMESPACE #endif // QCOMPAREHELPERS_H diff --git a/src/corelib/itemmodels/qabstractitemmodel.cpp b/src/corelib/itemmodels/qabstractitemmodel.cpp index cd29f2fcc2a..629ee9582af 100644 --- a/src/corelib/itemmodels/qabstractitemmodel.cpp +++ b/src/corelib/itemmodels/qabstractitemmodel.cpp @@ -1158,6 +1158,7 @@ void QAbstractItemModel::resetInternalData() \ingroup model-view + \compares strong This class is used as an index into item models derived from QAbstractItemModel. The index is used by item views, delegates, and @@ -1328,24 +1329,22 @@ void QAbstractItemModel::resetInternalData() */ /*! - \fn bool QModelIndex::operator==(const QModelIndex &other) const + \fn bool QModelIndex::operator==(const QModelIndex &lhs, const QModelIndex &rhs) - Returns \c{true} if this model index refers to the same location as the - \a other model index; otherwise returns \c{false}. + Returns \c{true} if \a lhs model index refers to the same location as the + \a rhs model index; otherwise returns \c{false}. The internal data pointer, row, column, and model values are used when comparing with another model index. */ - /*! - \fn bool QModelIndex::operator!=(const QModelIndex &other) const + \fn bool QModelIndex::operator!=(const QModelIndex &lhs, const QModelIndex &rhs) - Returns \c{true} if this model index does not refer to the same location as - the \a other model index; otherwise returns \c{false}. + Returns \c{true} if \a lhs model index does not refer to the same location as + the \a rhs model index; otherwise returns \c{false}. */ - /*! \fn QModelIndex QModelIndex::parent() const @@ -4124,10 +4123,10 @@ bool QAbstractListModel::dropMimeData(const QMimeData *data, Qt::DropAction acti */ /*! - \fn bool QModelIndex::operator<(const QModelIndex &other) const + \fn bool QModelIndex::operator<(const QModelIndex &lhs, const QModelIndex &rhs) \since 4.1 - Returns \c{true} if this model index is smaller than the \a other + Returns \c{true} if \a lhs model index is smaller than the \a rhs model index; otherwise returns \c{false}. The less than calculation is not directly useful to developers - the way that indexes diff --git a/src/corelib/itemmodels/qabstractitemmodel.h b/src/corelib/itemmodels/qabstractitemmodel.h index 8f22f14989e..1e9c58334de 100644 --- a/src/corelib/itemmodels/qabstractitemmodel.h +++ b/src/corelib/itemmodels/qabstractitemmodel.h @@ -5,11 +5,14 @@ #ifndef QABSTRACTITEMMODEL_H #define QABSTRACTITEMMODEL_H +#include #include #include #include #include +#include + QT_REQUIRE_CONFIG(itemmodel); QT_BEGIN_NAMESPACE @@ -138,19 +141,16 @@ public: inline QVariant data(int role = Qt::DisplayRole) const; inline void multiData(QModelRoleDataSpan roleDataSpan) const; inline Qt::ItemFlags flags() const; - constexpr inline const QAbstractItemModel *model() const noexcept { return m; } + constexpr inline const QAbstractItemModel *model() const noexcept { return m.get(); } constexpr inline bool isValid() const noexcept { return (r >= 0) && (c >= 0) && (m != nullptr); } - constexpr inline bool operator==(const QModelIndex &other) const noexcept - { return (other.r == r) && (other.i == i) && (other.c == c) && (other.m == m); } - constexpr inline bool operator!=(const QModelIndex &other) const noexcept - { return !(*this == other); } - constexpr inline bool operator<(const QModelIndex &other) const noexcept - { - return r < other.r - || (r == other.r && (c < other.c - || (c == other.c && (i < other.i - || (i == other.i && std::less()(m, other.m)))))); - } + +private: + constexpr auto asTuple() const noexcept { return std::tie(r, c, i, m); } + friend constexpr bool comparesEqual(const QModelIndex &lhs, const QModelIndex &rhs) noexcept + { return lhs.asTuple() == rhs.asTuple(); } + friend constexpr Qt::strong_ordering compareThreeWay(const QModelIndex &lhs, const QModelIndex &rhs) noexcept + { return QtOrderingPrivate::compareThreeWayMulti(lhs.asTuple(), rhs.asTuple()); } + Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(QModelIndex) private: inline QModelIndex(int arow, int acolumn, const void *ptr, const QAbstractItemModel *amodel) noexcept : r(arow), c(acolumn), i(reinterpret_cast(ptr)), m(amodel) {} @@ -158,7 +158,7 @@ private: : r(arow), c(acolumn), i(id), m(amodel) {} int r, c; quintptr i; - const QAbstractItemModel *m; + Qt::totally_ordered_wrapper m; }; Q_DECLARE_TYPEINFO(QModelIndex, Q_RELOCATABLE_TYPE); diff --git a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp index 6b1e4ce9baf..3c46c519f88 100644 --- a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp +++ b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp @@ -993,7 +993,7 @@ void tst_QAbstractItemModel::complexChangesWithPersistent() void tst_QAbstractItemModel::modelIndexComparisons() { - QTestPrivate::testEqualityOperatorsCompile(); + QTestPrivate::testAllComparisonOperatorsCompile(); QTestPrivate::testEqualityOperatorsCompile(); QTestPrivate::testEqualityOperatorsCompile(); @@ -1006,6 +1006,9 @@ void tst_QAbstractItemModel::modelIndexComparisons() QT_TEST_EQUALITY_OPS(mi11, mi11, true); QT_TEST_EQUALITY_OPS(mi11, mi22, false); + QT_TEST_ALL_COMPARISON_OPS(mi11, mi11, Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(mi11, mi22, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(mi22, mi11, Qt::strong_ordering::greater); QT_TEST_EQUALITY_OPS(pmi11, pmi11, true); QT_TEST_EQUALITY_OPS(pmi11, pmi22, false); QT_TEST_EQUALITY_OPS(pmi11, mi11, true);