summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Solovev <[email protected]>2025-06-03 10:57:26 +0200
committerIvan Solovev <[email protected]>2025-06-05 16:42:43 +0200
commit5b03d9f68b52d89a0c954ed585e530213969633f (patch)
tree4506b318384d9fc22191506b4aa74af518f2825c
parent9a950b1ecb901cc885995d128768cc04cb55cfb1 (diff)
QProperty: fix comparison with comparable typeHEADdev
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<T>, U), where operator==(T, U) exists. Explicitly delete operator==(QProperty<T>, QProperty<U>) 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 <[email protected]>
-rw-r--r--src/corelib/kernel/qproperty.h34
-rw-r--r--tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp13
2 files changed, 47 insertions, 0 deletions
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<T>
return false;
}
+ template <typename U, typename = void>
+ struct has_operator_equal_to : std::false_type{};
+
+ template <typename U>
+ struct has_operator_equal_to<U, std::void_t<decltype(bool(std::declval<const T&>() == std::declval<const U&>()))>>
+ : std::true_type{};
+
+ template <typename U>
+ static constexpr bool has_operator_equal_to_v =
+ !std::is_same_v<U, T> && has_operator_equal_to<U>::value;
+
public:
using value_type = typename QPropertyData<T>::value_type;
using parameter_type = typename QPropertyData<T>::parameter_type;
@@ -388,6 +399,23 @@ public:
QT_DECLARE_EQUALITY_OPERATORS_HELPER(QProperty, T, /* non-constexpr */, noexcept(false), template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>)
QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(QProperty, T, /* non-constexpr */, noexcept(false), template <typename Ty = T, std::enable_if_t<QTypeTraits::has_operator_equal_v<Ty>>* = nullptr>)
+ QT_DECLARE_EQUALITY_OPERATORS_HELPER(QProperty, U, /* non-constexpr */, noexcept(false), template <typename U, std::enable_if_t<has_operator_equal_to_v<U>>* = nullptr>)
+ QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(QProperty, U, /* non-constexpr */, noexcept(false), template <typename U, std::enable_if_t<has_operator_equal_to_v<U>>* = nullptr>)
+
+ // Explicitly delete op==(QProperty<T>, QProperty<U>) 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 <typename U, std::enable_if_t<!std::is_same_v<T, U>>* = nullptr>
+ friend void operator==(const QProperty &, const QProperty<U> &) QPROPERTY_DECL_DELETED_EQ_OP;
+ template <typename U, std::enable_if_t<!std::is_same_v<T, U>>* = nullptr>
+ friend void operator!=(const QProperty &, const QProperty<U> &) 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 <typename U, std::enable_if_t<has_operator_equal_to_v<U>>* = 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<QProperty<qsizetype>, int>();
+ QTestPrivate::testEqualityOperatorsCompile<QProperty<qsizetype>, double>();
+
+ QProperty<qsizetype> 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