[Development] How qAsConst and qExchange lead to qNN
marc.mutz at qt.io
Wed Nov 9 12:49:31 CET 2022
On 09.11.22 10:15, Volker Hilsheimer wrote:
> But I do believe that we can add APIs that are iterator and ranges friendly to Qt without tossing out the baby with the bathwater and without breaking tons of code and patterns. For APIs where we simply return a stored QList we want to be able to return that QList as a shared copy. If we would only return a std::span as a view on the stored list, then client code will need to construct their copy from that std::span. That is equally silly.
There are two assumptions you are making here, and it's important to
understand them when discussing NOI:
First, that when we return a copy of a stored QList, most users would
keep the data around for longer.
I don't have the stats to prove it, but I'd say that that's a corner
case, not the general case. Even when talking about storing the data
temporarily in a function, no owning container is required. And if
there's no Qt API that requires you to pass owning containers, passing
from and to Qt API also drops out of the use-cases that require
returning a shared copy of a QList.
Second, that the data is stored in a QList in the first place.
Even most Qt types don't have a need for CoW semantics of QList in their
implementation. The perceived need is coming solely from having to hand
out owning containers in the API, an operation we don't want to
deep-copy. So, catch-22.
But imagine you /don't/ need to hand out owning containers from your
API, what container would you choose, then? Well, QRegion chose
variant<QRect, QVector<QRect>>, and I've repeatedly shown how that
caused users to have to use rectCount() and boundingRect() to avoid the
conversion of the first into the latter option.
QVersionNumber uses SBO to store short version numbers inline, only
falling back to QList if the number isn't representable as a qint8.
The implementation is truly horribly complex, and the API is more
limited (the user can't iterate over the segments) than it would be if
it had not used SBO.
I'd wager that a lot more Qt types would rather benefit from a non-CoW
SBO container than benefit from the non-SBO CoW ones we currently have.
And the use of QList in the API is holding those types back from using the
optimal data structure, because QList is under compatibility constraints, so
even where we _have_ an SBO container (e.g. std::string as a stand-in for
QByteArray or std::u16string for QString), we can't use it, because the API uses
owning Qt containers and we can't change them. Or we do, and then we
continue the vicious cycle of Qt container churn on each major release.
NOI can break that cycle, so the Qt containers don't need to change,
keeping existing users happy, and both the implementations and users of
our API are free to choose whatever data structure best fits their
needs, making Qt developers and new users happy.
Marc Mutz <marc.mutz at qt.io>
Principal Software Engineer
The Qt Company
Erich-Thilo-Str. 10 12489
Geschäftsführer: Mika Pälsi, Juha Varelius, Jouni Lintunen
Sitz der Gesellschaft: Berlin,
Registergericht: Amtsgericht Charlottenburg,
HRB 144331 B
More information about the Development