[Development] How qAsConst and qExchange lead to qNN
marc.mutz at qt.io
Mon Nov 14 17:22:44 CET 2022
On 11.11.22 09:35, Ulf Hermann via Development wrote:
> There is an undeniable benefit of _offering_ QSpan, QStringView, and
> generator APIs in a few relevant cases:
> 1. Users want to pass a "foreign" container to a Qt function that
> doesn't only store it as QList or QString. It might merely iterate it or
> store it as something else.
The assumption that there's a problem only for "foreign containers" is
incorrect: Take the native Qt container QString as an example. See
assembly in my QAnyStringView blog post:
https://www.qt.io/blog/qstringview-diaries-qanystringview You have this
problem as soon as you pass constant data, which is a common enough
use-case to warrant optimizing for.
Aside: I find it amusing when people complain that span<>s aren't as
optimal as (ptr, n) on the broken Windows ABI, but then say that all
APIs should take owning containers instead. Either we care about
effciency of argument passing, or not. IMHO, spans strike the optimal
balance between efficiency and convenience.
The next major problem is subsetting. The Qt containers (QString,
QByteArray, QList) don't have efficient subsetting. Any form of parsing
thus directly benefits from views, and the end of parsing will then
naturally be tokens stored in views. If relevant consumer APIs don't
take views, but owning containers, you're likewise injecting owning
container constructions in user code, let alone memory allocations. A
good API should not have such impedance mismatches between its parsers
and its data consumers. And please, don't shoot the messenger.
QStringRef was trying to solve the same problem, just badly (being tied
to QString without being able to produce a QString from a substring w/o
Yes, there's fromRawData(), but it doesn't remove the ctor and dtor
calls of the owning container. And apart from QStringLiteral, no-one uses it. So even if the class stores data in
native containers, the construction of these native containers is often
better done centrally instead of being duplicated in each caller.
To summarize: the deep copy often happens already, in user code. By
using spans, the deep copy still happens, but the code to do so isn't
> All other cases look much fuzzier to me. QSpan or QStringView (or a
> generator) may be beneficial or detrimental there, depending on exact
> usage pattern. The cost we're avoiding is mostly the reference count
> a far cry from a deep copy.
This is not correct. The ref count of owning containers is certainly not
the main reason to use views. Otherwise non-implicitly-shared containers
would be a solutions, which they are not.
As detailed above, we're mainly avoiding the code bloat of owning
container construction and destruction, as well as the deep-copy on
subsetting. We also avoid accidental detaches
for (auto [pos, col] : gradient.stops())
and allow the implementation of a class to choose an optimal data
structure without causing an impedance mismatch with its own API.
I find it amusing when some people say that the use of NOI constrains
implementations when the opposite is demonstrably true (QRegion).
Both owning and non-owning interfaces have their advantages and
disadvantages. Huge code bases like llvm show that you can program just
as well with NOI (ArrayRef/StringRef) as with owning containers.
And I think at least for setters, the benefits of NOI far outweigh the
drawbacks. The only drawback I have heard is deep copy. From the same
people that suggest to favour convenience over efficiency. Well, for
setters, nothing is more convenient than NOI.
Can we agree that NOI for setters is a no-brainer? Then 90% of the
usefulness of NOI can already be reaped, in a BC and SC manner. There's
pretty little we can do with return values before Qt 7, except use the
stuff in private APIs to try it out. But we can and should convert
> On the flip side we're introducing complex
> life time problems that will lead to hard to find memory management
Spans add no lifetime problems on top of .data() and/or .begin()/.end().
In particular, these are statically detectable (cf.
implemented in VS 2019 and (experimentally) in Clang), so they're not
"hard to find".
> I suggest we look at this from the perspective of a _user_ of
> Qt. I'm pretty sure you can all imagine which problem a user would
> prefer here.
> So, I suggest we add those "view" APIs to the cases where they provide a
> clear benefit. For methods that return a span/view/generator, we should
> always offer a safe alternative that returns an owning container. The
> naming convention should be:
> 1. Use overloads for methods that take views or spans. In new API we can
> omit the methods that take owning containers. If the overload set grows
> out of hand, don't add the view/span alternative until we can remove
> something. By Thiago's argument, that means not to convert existing
> methods to QStringView for now.
> 2. Use the postfix "View", "Span" or "Generator" for methods that return
> views, spans or generators rather than owning containers. This way it's
> harder for users to mess up the life time.
This reminds me of an interview that Bjarne gave a few years back where
he recapped, and I'm paraphrasing here, that the committee usually
insists on extra syntax for new features, to the detriment of users that
are then agonizing over the complicated mess.
All you're doing by having foo() and fooSpan() is putting the burden of
using the correct form on the user instead of putting it where it
belongs: in the compiler.
This stuff is solved by tools, not documentation and uglified APIs.
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