[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