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

Mutz, Marc marc at kdab.com
Tue May 21 10:27:56 CEST 2019


Hi Lars,

There is a fine line between a proper partially-formed state and 
basically folding std::optional into every value class.

The former is a compile-time state. It is not observable at runtime, 
because any code which would observe a difference invokes UB. This is 
the same principle as C++11 data races, btw: Sequential Consistency for 
Data-Race-Free Programs.

The latter is a run-time property. As such, it will cost cpu cycles, 
death by a thousand paper cuts, sure, but cost. The cost is not 
necessarily in the extra branch, it's mostly in impairng the compiler's 
ability to optimize on the impossiblity of UB, which is important for, 
amongst other things, dead code removal. And while the partially-formed 
state can be extended to non-pimpled classes easily:

     class QRect {
        int x, y, w, h;
     public:
        QRect() = default;
     };
     QRect r; // partially-formed
     r.x();   // compilers _already_ warn about this
     QRect r = {}; // zero-initialized

QRect::isValid() has always been weird API, since it needs to reserve 
some valid, but non-normalized, values (remember, there's 
QRect::normalize()), to implement an optional absence of a rect. That 
should be modelled by optional<QRect>, not by QRect itself.

To summarize: the partially-formed state is a compile-time only concept 
with no runtime cost and is applicable to all C++ types (from int over 
structs to Qt classes). It is consistent across all types, improves 
performance as well as readability of source code, and can be detected 
both at runtime (with asserts) and compile time (with static analysis).

A d == nullptr state as a valid value is a runtime property, has 
associated costs, and makes source code harder to understand.

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.

Would that find your approval?

Thanks,
Marc


More information about the Development mailing list