[Development] What's the status of a moved-from object?

Mutz, Marc marc at kdab.com
Tue May 21 13:19:24 CEST 2019


On 2019-05-21 12:52, Julien Cugnière wrote:
> Le mar. 21 mai 2019 à 10:32, Mutz, Marc via Development
> <development at qt-project.org> a écrit :
>> It is, however, be an acceptable stop-gap measure, and here a 
>> compromise
>> may emerge, for keeping old code working while preparing for a
>> fully-implemented partially-formed state. This would mean we do assign
>> meaning to a nullptr d consistent with the documented default value in
>> Qt 5, but deprecate the use of the default ctor for establishing said
>> value in the docs and warn about the use of a default-constructed 
>> object
>> other than for destruction and assignment at runtime, even in release
>> mode. In Qt 7, we then crash, as we do for some moved-from objects
>> already.
> 
> Hi, I'm just a bystander, but I'm curious about this, and I wonder if
> I misunderstood.
> 
> Are you saying that in modern C++, the recommendation is that the
> default constructor of classes should leave the object in an
> uninitialized state, where most member functions called will crash or
> lead to undefined behavior? Do you have any links on the subject?

This has nothing to do with 'Modern C++'. It's how an int behaves since 
the dawn of time:

    int i; // uninited, can only be assigned to, or go out of scope 
(destroyed)
    int i = {}; // zero-initialized

In 2009 Alex Stepanov et al. wrote a little book called Elements of 
Programming in which they formalized this behaviour, calling it the 
Partially-Formed State, and extending it to all other value classes. 
Crucially, this was before C++ got move semantics. To my knowledge, 
https://www.kdab.com/stepanov-regularity-partially-formed-objects-vs-c-value-types/ 
is the first treatise that extends EoP's notion to C++11 and, in 
particular, move semantics.

A type doesn't need to establish just the partially-formed state. But 
users of types (and generic algorithms) may not assume more than that, 
either.

> Are you suggesting that in Qt 7, we will have the following :
> 
>     QRect r;
>     qDebug() << r.width(); // random value

No: UB. But also:

Compiler warning: r.m_w might be used uninitialised in this function.

>     QRect r = {};
>     qDebug() << r.width(); // OK, prints 0

>     QVector<int> v;
>     qDebug() << v.size(); // crash

No. QVector is a container, their default is empty, and that's all 
right, because their default ctor already does the minimum amount of 
work necessary for the dtor to run. There's also no extra runtime check 
involved: size() { return begin() - end(); } where begin() == nullptr 
and end() == nullptr. And yes, just as now, v.front() invokes UB.

std::list is more interesting...

>     QString s;
>     qDebug() << s.indexOf("foo"); // crash

No the second. QString, too, is a container.

> What about std::string and std::vector ?

They, too, are containers. And no, the standard does not use the 
partially-formed state. As a consequence, we have now 
std::variant::valueless_by_exception.

For more info, read EoP (it's out of print, though, and my copy ain't 
for sale) or my article linked above.

Thanks,
Marc


More information about the Development mailing list