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<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 <fabian.kosmale@qt.io>
This commit is contained in:
parent
9a950b1ecb
commit
5b03d9f68b
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user