diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 26cbd80fb9c..813ccdb6735 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -420,28 +420,17 @@ bool QObject::setProperty(const char *name, QVariant &&value) template inline T qobject_cast(QObject *object) { - static_assert(std::is_pointer_v, - "qobject_cast requires to cast towards a pointer type"); - typedef typename std::remove_cv::type>::type ObjType; - static_assert(QtPrivate::HasQ_OBJECT_Macro::Value, - "qobject_cast requires the type to have a Q_OBJECT macro"); - return static_cast(ObjType::staticMetaObject.cast(object)); + return QtPrivate::qobject_cast_helper(object); } template inline T qobject_cast(const QObject *object) { - static_assert(std::is_pointer_v, - "qobject_cast requires to cast towards a pointer type"); static_assert(std::is_const_v>, "qobject_cast cannot cast away constness (use const_cast)"); - typedef typename std::remove_cv::type>::type ObjType; - static_assert(QtPrivate::HasQ_OBJECT_Macro::Value, - "qobject_cast requires the type to have a Q_OBJECT macro"); - return static_cast(ObjType::staticMetaObject.cast(object)); + return QtPrivate::qobject_cast_helper(object); } - template constexpr const char * qobject_interface_iid() = delete; template inline T * qobject_iid_cast(QObject *object, const char *IId = qobject_interface_iid()) diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 3bff5c3393c..b369d2ed8f7 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -737,6 +737,24 @@ namespace QtPrivate { static int test(int (Object::*)(QMetaObject::Call, int, void **)); enum { Value = sizeof(test(&Object::qt_metacall)) == sizeof(int) }; }; + + template + inline TgtType qobject_cast_helper(SrcType *object) + { + using ObjType = std::remove_cv_t> ; + static_assert(std::is_pointer_v, + "qobject_cast requires to cast towards a pointer type"); + static_assert(HasQ_OBJECT_Macro::Value, + "qobject_cast requires the type to have a Q_OBJECT macro"); + + if constexpr (std::is_final_v) { + if (object && object->metaObject() == &ObjType::staticMetaObject) + return static_cast(object); + return nullptr; + } else { + return static_cast(ObjType::staticMetaObject.cast(object)); + } + } } QT_END_NAMESPACE diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 6c804044378..609229b1ae7 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -92,6 +92,7 @@ private slots: void dumpObjectTree(); void connectToSender(); void qobjectConstCast(); + void qobjectCastFinal(); void uniqConnection(); void uniqConnectionPtr(); void interfaceIid(); @@ -2429,6 +2430,13 @@ public: int rtti() const override { return 43; } }; +class FinalObject final: public FooObject +{ + Q_OBJECT +public: + int rtti() const override { return 44; } +}; + void tst_QObject::declareInterface() { FooObject obj; @@ -3691,9 +3699,29 @@ void tst_QObject::qobjectConstCast() const QObject *cptr = &obj; QVERIFY(qobject_cast(ptr)); + QVERIFY(qobject_cast(ptr)); QVERIFY(qobject_cast(cptr)); } +void tst_QObject::qobjectCastFinal() +{ + FooObject foo; + QObject *ptr = &foo; + const QObject *cptr = &foo; + + QCOMPARE(qobject_cast(ptr), nullptr); + QCOMPARE(qobject_cast(ptr), nullptr); + QCOMPARE(qobject_cast(cptr), nullptr); + + FinalObject final; + ptr = &final; + cptr = &final; + + QCOMPARE(qobject_cast(ptr), &final); + QCOMPARE(qobject_cast(ptr), &final); + QCOMPARE(qobject_cast(cptr), &final); +} + void tst_QObject::uniqConnection() { SenderObject s;