diff options
author | Ivan Solovev <[email protected]> | 2025-06-03 10:57:26 +0200 |
---|---|---|
committer | Ivan Solovev <[email protected]> | 2025-06-05 16:42:43 +0200 |
commit | 5b03d9f68b52d89a0c954ed585e530213969633f (patch) | |
tree | 4506b318384d9fc22191506b4aa74af518f2825c | |
parent | 9a950b1ecb901cc885995d128768cc04cb55cfb1 (diff) |
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.h | 34 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp | 13 |
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 |