[Development] shadowing properties (and methods)

Ulf Hermann ulf.hermann at qt.io
Tue Feb 24 15:57:39 CET 2026


Greetings and Salutations,

As some of you may know, QMetaObject has a problem with what we call 
"shadowing". Consider:

class Base
{
     Q_OBJECT
     Q_PROPERTY(int a READ a CONSTANT)

public:
     int a() const { return 4; }

     Q_INVOKABLE int b() const { return 5; }
};

class Derived : public Base
{
     Q_OBJECT
     Q_PROPERTY(int a READ a CONSTANT) // shadows Base::a

public:
     int a() const { return 6; }

     // Shadows Base::b
     Q_INVOKABLE int b() const { return 7; }
};

Now when you get a Base* and retrieve its property "a" or call its 
method "b" via the QMetaObject, you don't actually know which property 
you are retrieving or which method you are calling.

 From C++ you can introspect the QMetaObject hierarchy and determine 
what property or method you're dealing with. This is much more 
complicated than the usual way of simply doing 
someObject->property("a"), though.

 From QML, you can't. QML will always use the most-derived QMetaObject 
that has the property or method. You can shadow properties and methods 
further in QML, and most egregiously, you can shadow properties with 
methods and vice versa. This is rather dangerous because we internally 
rely on many of the properties of, say QQuickItem, to actually assume 
the values we set on them. If you shadow a property like 
QQuickItem::data, you're certainly in for some surprises. It is quite 
common to add a property called "data" to one of your custom types, 
though. So, this is inviting some serious foot archery. For fun and 
confusion, try the following:

// Base.qml
import QtQuick
Item {
    id: root
    property int data
}

// Derived.qml
import QtQuick
Base{
    Rectangle {}
}

This results in the following warning:

Derived.qml:3:4: Cannot assign value of type "Rectangle" to property 
"data", expecting "int"

We've recognized this as a problem a long time ago and introduced the 
FINAL attribute to Q_PROPERTY. The FINAL attribute only takes effect if 
you use QML. If you shadow a FINAL property in pure C++, it does nothing 
(unfortunately). However, if you try to use a *shadowed* FINAL property 
in QML you get a warning or an error, depending on the context. We can't 
really do better than this for FINAL alone. You can call moc on Base and 
Derived independently, and you can re-moc Base after building Derived. 
This way you can sneakily introduce a FINAL after the derived type 
already exists. So we have to accept some fuzziness there. It follows 
that also in QML we can't flat out reject all your shadowed properties. 
We can just assume they are not shadowed and still use the base type's 
properties in case they are.

On top of all this complication, however, the presence of FINAL is not 
quite enough because you have to remember to add FINAL to every property 
that's eligible for finality. Keeping track of this is messy and 
error-prone. As a result, few properties are FINAL in practice, even if 
most should be.

C++, back in the day, had a similar problem with virtual methods. When 
you declared a method in a derived type, you'd have to look up all the 
methods in all base types to figure out if the method you were declaring 
was virtual by inheritance. This was error-prone. You could accidentally 
introduce overriding methods that weren't meant to be virtual and vice 
versa.

In C++, this was solved by introducing the "override" and "final" 
keywords to mark methods that are intentionally overriding a virtual 
one. The introduction of the "override" and "final" keywords came with a 
number of warnings and errors that now steer you towards adding the new 
keywords to all methods that intentionally override, and only those.

In Qt 6.11, we're doing the same to Q_PROPERTY. In addition to FINAL, 
you can now add attributes VIRTUAL and OVERRIDE to mark properties that 
are intended to be overridden and ones that intentionally override a 
virtual property. Likewise, in QML you can mark properties with the 
"virtual", "override", and "final" keywords. If you use those new 
attributes and keywords, you get appropriate warnings and errors in QML 
(but not in C++, because see above). If you don't use them, you don't 
get warned ... yet. This is because we haven't adapted all of Qt to be 
free of those warnings, yet.

We still highly recommend you clean up unintended shadowing and use the 
new keywords and attributes where applicable already in Qt 6.11. You can 
get warned about shadowing properties without the new keywords and 
attributes by enabling debug messages from the 
qt.qml.propertyCache.append logging category. For example with an 
environment variable like this:

QT_LOGGING_RULES="qt.qml.propertyCache.append.debug=true"

For Qt 6.12, we are planning to enable these warnings by default. Among 
other things, this will mean that you will get warned when shadowing 
properties from Qt types that aren't meant to be shadowed.

In addition, we are planning to resolve the issue of shadowing methods 
and shadowing properties with methods in a similar way. We don't know 
when this will be ready, though.

best regards,
Ulf


More information about the Development mailing list