From 5b03d9f68b52d89a0c954ed585e530213969633f Mon Sep 17 00:00:00 2001 From: Ivan Solovev Date: Tue, 3 Jun 2025 10:57:26 +0200 Subject: [PATCH] QProperty: fix comparison with comparable type After e115c60b6da0db7013229e678720f36632c2e614, the comparison with a type which is different from QProperty::value_type, but is comparable to it, could result in ambiguous operator==() overloads. Fix it by adding a new overload for operator==(QProperty, U), where operator==(T, U) exists. Explicitly delete operator==(QProperty, QProperty) for such types T and U, because the implicit conversion might be unwanted here. The user should manually call .value() at least on one property, if they want the comparison. Note that GCC does not allow to do it, treating `= delete` as declaration and complaining about a default template argument in friend template declaration. So, do it only for Clang and MSVC. Amends e115c60b6da0db7013229e678720f36632c2e614. Task-number: QTBUG-134921 Pick-to: 6.10 Change-Id: Id3ed48738cc462b5b0820fa3b25d80d4d4414548 Reviewed-by: Fabian Kosmale --- src/corelib/kernel/qproperty.h | 34 +++++++++++++++++++ .../kernel/qproperty/tst_qproperty.cpp | 13 +++++++ 2 files changed, 47 insertions(+) diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 4a144911771..f4e7942e947 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -360,6 +360,17 @@ class QProperty : public QPropertyData return false; } + template + struct has_operator_equal_to : std::false_type{}; + + template + struct has_operator_equal_to() == std::declval()))>> + : std::true_type{}; + + template + static constexpr bool has_operator_equal_to_v = + !std::is_same_v && has_operator_equal_to::value; + public: using value_type = typename QPropertyData::value_type; using parameter_type = typename QPropertyData::parameter_type; @@ -388,6 +399,23 @@ public: QT_DECLARE_EQUALITY_OPERATORS_HELPER(QProperty, T, /* non-constexpr */, noexcept(false), template >* = nullptr>) QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(QProperty, T, /* non-constexpr */, noexcept(false), template >* = nullptr>) + QT_DECLARE_EQUALITY_OPERATORS_HELPER(QProperty, U, /* non-constexpr */, noexcept(false), template >* = nullptr>) + QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(QProperty, U, /* non-constexpr */, noexcept(false), template >* = nullptr>) + + // Explicitly delete op==(QProperty, QProperty) for different T & U. + // We do not want implicit conversions here! + // However, GCC complains about using a default template argument in a + // friend declaration, while Clang and MSVC are fine. So, skip GCC here. +#if !defined(Q_CC_GNU) || defined(Q_CC_CLANG) +#define QPROPERTY_DECL_DELETED_EQ_OP \ + Q_DECL_EQ_DELETE_X("Call .value() on one of the properties explicitly.") + template >* = nullptr> + friend void operator==(const QProperty &, const QProperty &) QPROPERTY_DECL_DELETED_EQ_OP; + template >* = nullptr> + friend void operator!=(const QProperty &, const QProperty &) QPROPERTY_DECL_DELETED_EQ_OP; +#undef QPROPERTY_DECL_DELETED_EQ_OP +#endif // !defined(Q_CC_GNU) || defined(Q_CC_CLANG) + parameter_type value() const { d.registerWithCurrentlyEvaluatingBinding(); @@ -520,6 +548,12 @@ private: return lhs.value() == rhs; } + template >* = nullptr> + friend bool comparesEqual(const QProperty &lhs, const U &rhs) + { + return lhs.value() == rhs; + } + void notify() { d.notifyObservers(this); diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index 53a477edc22..aadcea9b9e8 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -84,6 +84,7 @@ private slots: void compatPropertySignals(); void compareAgainstValueType(); + void compareAgainstDifferentType(); void noFakeDependencies(); #if QT_CONFIG(thread) @@ -1771,6 +1772,18 @@ void tst_QProperty::compareAgainstValueType() QCOMPARE_NE(vl, o.varList); } +void tst_QProperty::compareAgainstDifferentType() +{ + QTestPrivate::testEqualityOperatorsCompile, int>(); + QTestPrivate::testEqualityOperatorsCompile, double>(); + + QProperty p1{1}; + QCOMPARE_EQ(p1, 1); + QCOMPARE_EQ(1, p1); + QCOMPARE_NE(p1, 2.0); + QCOMPARE_NE(2.0, p1); +} + class FakeDependencyCreator : public QObject { Q_OBJECT