[Development] QString and related changes for Qt 6

Matthew Woehlke mwoehlke.floss at gmail.com
Wed May 13 17:17:03 CEST 2020


On 12/05/2020 17.21, Thiago Macieira wrote:
> On Tuesday, 12 May 2020 08:42:28 PDT Matthew Woehlke wrote:
>> How will this work? As I understand, the main advantage to
>> QStringLiteral is that it statically encodes the *length* as well as the
>> data. This isn't possible with raw literals, which are merely
>> NUL-terminated.
> 
> Black magic!
> 
> I mean, templates and constexpr.

Yeah... I'm not sure what I was thinking when I wrote that...

Oh, wait...

>> I don't see us ever getting rid of some form of QString
>> literal short of templatizing *everything* that takes a T* (for T in
>> char, char16_t, etc.) to take a T(&)[N] instead.

...I was thinking this. You might be able to escape this for methods 
that don't take both QString *and* QStringView. Otherwise, well, see my 
later message on that point.

And on that note...

> But QStringView(u"foo") should call that first constructor. Doesn't it? I
> never remember if the literal decays to pointer before the overload
> resolution.

Uh... no, actually it doesn't. (Which TBH smells a bit like a defect to 
me, but we're stuck with it for now.)

Note: https://godbolt.org/z/FbjQkM

(That was experimenting with QString/QStringView overload 
disambiguation, but also includes the relevant ctors. Comment out the 
templated overload of `foo` and one of the others, and you'll see that 
the invocation with a literal calls the "wrong" ctor.)

So, we either need to retain literals in some form, or, as I was saying, 
every method needs to have a templated flavor for string literals.

>> The "nice" thing about QStringView is that it does not have ownership;
>> you have to be careful about how long you hold onto it lest it turn into
>> a dangling pointer. You can't construct a QString from any old bag of
>> byt^Wcharacters because a QString is implicitly valid until it is destroyed.
> 
> That's the problem we've had with QStringLiteral and QString::fromRawData().
> 
> You *can* create it from read-only data and tell it never to try to modify.
> The trick is guaranteeing that it remains valid until the last user finished
> using it. Because of copy-on-write, that last user can be much later than the
> statement that created the QString in the first place.

Right, but if you're using QStringLiteral / QString::fromRawData, you 
"know" you're taking on that responsibility. (And for QStringLiteral, 
you only run into problems in some instances with library unloading, 
which is a non-issue for many applications.)

What I worry about with trying to avoid QStringView is that we either 
lose the ability to avoid copies when the input is a *temporary* (e.g. 
stack-allocated) buffer, or else we silently accept such uses and 
produce broken programs.

Note that you can't rely on adding non-const overloads as a work-around; 
the string might be coming from an intermediate function that doesn't 
have a non-const overload, but was called with a (non-const) temporary 
buffer.

Example:

   void foo(char const* s)
   {
     ...
     method_taking_qt_string(s);
     ...
   }

   void bar()
   {
     char buffer[MAX_SIZE];
     ...do stuff to put data in buffer...
     foo(buffer);
   }

Non-owning QString is dangerous. QStringLiteral is less dangerous 
because it is almost never used with non-rodata storage (and indeed, I 
would consider any such usage highly suspect, if not outright broken). 
QString::fromRawData is dangerous, but "obviously" so.

We should not implement any way of creating a non-owning QString that is 
not explicit, and if we adhere to that, I don't see us *not* wanting 
QStringView in many instances.

-- 
Matthew


More information about the Development mailing list