[Development] How qAsConst and qExchange lead to qNN

Marc Mutz marc.mutz at qt.io
Wed Nov 9 12:49:31 CET 2022

Hi Volker,

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[3]. 
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
Berlin, Germany

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 mailing list