[Development] HEADS UP: do not pass const objects to QObject::disconnect(const QMetaObject::Connection&)!
Marc Mutz
marc.mutz at qt.io
Mon Apr 13 14:41:09 CEST 2026
Hi,
(back from vacation)
On 08.04.26 10:27, Volker Hilsheimer via Development wrote:
Having followed the discussion on gerrit for a while, I agree with the following points made there:
- QMetaObject::Connection is a handle- or pointer-like type
There is no reason for it to become move-only (it’s not an owning handle); and as with all handles, the handled object might change even if the handle itself is const. This might result in a change of an observable state.
Connection is a shared_ptr when it could be a unique_ptr, that's all. There are very few existing users that actually _require_ copying. Mostly, it's missing std::move()s and use of QList and QHash instead of QVLA, std::vector, or std::unordered_map, all of which are fixable, and tend to improve the codegen. There's overhead in shared_ptr vs. the unique_ptr, and if we were to implement this API anew now, we would certainly make it move-only to begin with. Copyability is easily added on a per-use case with a shared_ptr. Don't pay for what you don't use.
There's also the question of the thread-safety of disconnect(Connection), which would also be trivial for a move-only type, but require atomic operations for a copyable one.
So no, we don't _need_ to make Connection move-only, but it would make the type easier to grok for new users and make code easier to follow, because there's only ever one owner.
Taking care not to confuse familiarity with simplicity, a move-only Connection is therefore just better API. Given we strive for good API, the question isn't whether Connection should be copyable (it shouldn't be), but whether there's a realistic path to making it move-only. Thus my suggestion to add the opt-in macro to disable copyability, add it to STRICT_MODE, and then, in Qt 7 and Qt 8, see whether we can make it permanent.
- making the modified member mutable strikes me as the best tradeoff: it solves the UB, while not forcing people to modify working and compiling code
That is essentially https://codereview.qt-project.org/c/qt/qtbase/+/721918, once the comments to the commit message are taken into account.
I don't disagree. I'm just saying this bug is special in that it can be fixed on the user side. Assuming the project is in a regulated industry, changing user code ought to be easier than changing the Qt version. Also, Qt 5 users won't get a new Qt version, but they can still fix their code. Not many bugs are like that. The absence of a need for a patched Qt makes this special, thus I raised it on the MLs.
We have zero data points suggesting that any current compiler would optimize the clearing of the member away, or even optimize out the actual disconnect. Nobody has so far produced any (however contrived, such as above) scenario in which this happens. IOW, the risk of this issue causing any real problems is practically zero.
This is not how UB works, unfortunately. Ask the Linux kernel guys about GCC silently removing their buffer overflow guards some years back :( We would also not necessarily see bug reports, because the likely effect is that a disconnect() doesn't happen at the manually-specified time, but only later, at source or receiver object destruction, and the likely problem there is further UB such as invalid downcasts, of which we had many in Qt and probably continue to have many, without bug reports about them, either.
The main argument repeated in the discussion is that this fix requires a recompile as well as Qt upgrade. But given that the issue evidently only materializes with a hypothetical future compiler, a recompile would be needed for that to happen anyway. So that argument seems to bite its own tail.
The question is what to do though. I'm saying that we should do everything we can, but reviewers stop any one of the patches because it's not their favourite solution.
IMNSHO, we should:
- document that disconnect() modifies the argument
- publicize that just not passing const objects makes you immune (started here)
- add non-const overloads
- add an opt-out that disables the const ones
- make the member mutable, add a change-log that the alternative to recompilation is to not pass const objects
- add an opt-out that disables copying
(all the above can presumably be done in all active branches)
- deprecate the const overloads
- deprecate copying
- remove the const overloads
- remove copying
- un-mutable the member
- make/mark disconnect(Connection) thread-safe
This gives users the most flexibility, and lets both the Qt project and its users fall into the pit of success.
Thanks,
Marc
--
Marc Mutz <marc.mutz at qt.io><mailto:marc.mutz at qt.io> (he/his)
Principal Software Engineer
The Qt Company
Erich-Thilo-Str. 10 12489
Berlin, Germany
www.qt.io<http://www.qt.io>
Geschäftsführer: Mika Pälsi, Juha Varelius, Jouni Lintunen
Sitz der Gesellschaft: Berlin,
Registergericht: Amtsgericht Charlottenburg,
HRB 144331 B
Confidential
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/development/attachments/20260413/3c9de0c0/attachment-0001.htm>
More information about the Development
mailing list