[Development] parented_ptr
Volker Hilsheimer
volker.hilsheimer at qt.io
Wed Oct 29 16:47:28 CET 2025
> On 29 Oct 2025, at 12:00, Daniel Schürmann <daschuer at mixxx.org> wrote:
>>
>> > On 29 Oct 2025, at 01:42, Ville Voutilainen <ville.voutilainen at gmail.com> wrote:
>> >
>> > On Tue, 28 Oct 2025 at 23:15, Volker Hilsheimer <volker.hilsheimer at qt.io> wrote:
>> >>> Precisely so. Our ostensible declaration (and definition) is
>> >>>
>> >>> class QObject {
>> >>> ...
>> >>> public:
>> >>> template <class Child, class... Args>
>> >>> Child* makeChildObject(Args&&... args)
>> >>> {
>> >>> return new Child(std::forward<Args>(args)..., this);
>> >>> }
>> >>> };
>> >>>
>> >>> and that's all there is to it.
>> >>>
>> >>> The reason it's a reach too far to return some
>> >>> semantically-parented-pointer instead is adoptability.
>> >>> With this approach, you can just change
>> >>>
>> >>> MyType* var = new MyType(foo, bar, someParent);
>> >>>
>> >>> to
>> >>>
>> >>> MyType* var = someParent->makeChildObject<MyType>(foo, bar);
>> >>>
>> >>> and there's no need for heavier refactorings in how that code uses
>> >>> MyType*. It doesn't need to be refactored to
>> >>> use a parented_ptr everywhere.
>> >>
>> >>
>> >> The above pattern seems to me to be the most pragmatic approach for an API addition to Qt. Perhaps also a QObject::addChild(std::unique_ptr<QObject *> child) which moves the ownership of the pointer held by child into the callee.
>> >>
>> >>
>> >> I do see that a parented_ptr<> type makes the intent clear to readers, static analysers, AI tools etc, in the same way that std::move’ing a std::unique_ptr would. And that type then producing clear failures (compile time if possible, although in practice it will have to be runtime assertions) in case of incorrect usage might be practical. But that can be achieved via explicit declaration of the variable, e.g.
>> >>
>> >> parented_ptr<QLineEdit> input = dialog->makeChildWidget<QLineEdit>();
>> >> // would assert in ~parented_ptr if ‘input’ doesn’t have a parent
>> >>
>> >>
>> >> This makes the intent clear, while still allowing
>> >>
>> >> QLineEdit *input = dialog->makeChildWidget<QLineEdit>();
>> >
>> > Sounds like a plan. Would you do the honors of producing a patch for all that?
>> >
>> > And QParentedPointer instead of parented_ptr, right? :)
>>
>>
>> If “you” is “Volker”, then not in the near future - but hopefully this conversation has given Daniel some input.
>>
>> One thing to consider in this context is if/how we’d like QLayout to be part of this logic. When building a widget UI, QLayout is ultimately responsible for taking core of the correct parent/child relationships etc. Opinions vary on that topic, but I typically create child widgets without parent and rely on the layout. So, we might find out that a QLayout::makeChildWidget makes sense.
>>
>> Volker
>>
> that sounds great. I am happy to help. I will need a helping hand since I have no experience with the Qt contribution process.
Ping me directly if you need any help with the setup, no need to cause traffic on the list for that.
Setting up your gerrit user is documented here:
https://wiki.qt.io/Setting_up_Gerrit
For starters, you can make your life easier by only initializing and configuring qtbase, i.e.
% git clone git://code.qt.io/qt/qt5.git
% cd qt5
% ./init-repository -submodules qtbase
% cd qtbase
% git checkout dev
% git commit -m "test" --allow-empty
% git show
(should show a commit message with a Change-Id footer)
% git reset —hard origin/dev
Building from source is then documented here:
https://wiki.qt.io/Building_Qt_6_from_Git
> I am happy that it fits to my summary here:
> https://lists.qt-project.org/pipermail/development/2025-October/046669.html
>
> Listing things we have agreed with incorporated semantics (please correct me or confirm):
> QParentedPointer: A new pointer that points to a QObject with some parent != nullptr.
> QObject::makeChildObject(): That creates a parented Objects.
… and returns a Child * (not a QParentedPointer<Child>)
Adding each of those can be two separate commits.
> Borrow case (pChild must not outlive pDialog):
> Child *pChild = pDialog->makeChildObject<Child>()
> Member variable case (m_pChild is never dangling because the parent == this)
> QParentedPointer<Child> m_pChild = makeChildObject<Child>()
>
>
> Open Ideas:
>
> · Make QParentedPointer<Child> QObject::makeChildObject() protected
Why protected? Should be public so that one can write
QDialog *dialog = new QDialog;
auto buttons = dialog->makeChildWidget<QDialogButtonBox>(…);
And makeChildObject() needs to return a raw Child *, not a QParentedPointer.
> · Add a QObject::addChild(std::unique_ptr<QObject *> child)
That would then be a third commit.
> · Ideas around UniqueObjectPtrDeleter() which can be better implemented by Qt Object tree allowing shared ownership
> · makeQSharedPointer / makeQPointer …
Not convinced that we need these two, but one step at a time :)
> I think we have two interpretations we need to be explicit on to avoid later discussions:
>
> 1. QParentedPointer: Object has any parent -> there where objections, we already have QPointer()
> 2. QParentedPointer: Object has “this” parent -> some implicit lifetime guarantees
> My vote is 2. after reading all the helpful comments.What do you think?
QPointer is a weak pointer allowing you to monitor that a QObject is still alive. It doesn’t take ownership, and it doesn’t verify anything wrt the child having a parent.
And QParentedPointer as discussed so far has no knowledge of what “this” (or generally the parent) would be. Putting that information in there makes things overly complicated very clumsy to refactor (you’d end up maintaining a separate parent/child relationship in those pointers). In my mind, QParentedPointer’s only purpose is to make it explicit in code that the held object is expected to have a parent by the time the QParentedPointer variable goes out of scope; that expectation can be asserted via a Q_ASSERT in the destructor. Anything beyond that seems overkill.
I’d expect QParentedPointer to be primarily used in code where ownership isn’t clear (ie. in the snippet above, the auto could be QParentedPointer<QDialogButtonBox>). For child objects that are member variables of the parent object, the ownership is typically already rather clear (but nothing should stop people from using QPP in both places).
Volker
More information about the Development
mailing list