jandrewrogers 6 days ago

The standard idiom is to have a sentinel state for the object indicating it is invalid. For objects without trivial destructors or which may be read after being moved-from (a valid behavior in some systems code contexts) then you need a sentinel state anyway because moves in C++ are non-destructive.

C++ uses deferred destruction as a standard tool to solve a variety of problems.

3
jjmarr 6 days ago

> which may be read after being moved-from (a valid behaviour in some systems code contexts)

std::move as applied to standard library types will leave the object in a "valid but unspecified state".[1] If you're leaving the object in an invalid state (one where the invariants are broken), you're not writing idiomatic C++.

[1] https://en.cppreference.com/w/cpp/utility/move.html

jandrewrogers 6 days ago

I am using “invalid” here in the semantic sense of not containing a meaningful value. It is not invalid in a structural sense.

atq2119 5 days ago

This makes sense for objects that can enter an equivalent invalid state after successful construction as the result of a method call (e.g. a file or stream).

For objects that don't have that property, you're just exchanging one kind of badness in the design for a different but ultimately equivalent badness.

sunshowers 6 days ago

So the existence of an object of the type does not act as a static proof that the state is valid?

jandrewrogers 6 days ago

This is correct (and I am using “invalid” here in a semantic sense, it is still structurally valid). There are a number contexts in low-level systems code where a static proof is not possible even in theory, so there needs to be a way for code to inspect object validity at runtime. Process address space isn’t entirely private, external actors that your process doesn’t entirely control can modify it e.g. via DMA.

The C++ compiler largely assumes that such static proof is possible by default and has no way of knowing if it is not. To address this, the C++ language has added features for annotating objects to indicate that static proofs of state are not possible at compile-time (e.g. std::launder).

Database kernels are the most extreme example of this because most objects in the address space don’t own their memory address and the mechanism that temporarily puts an object at a particular memory address is not visible at compile-time. Consequently, object location and state has to be resolved dynamically at runtime.

sunshowers 6 days ago

Definitely agree that there's plenty of cases in systems code where static proofs are impossible. That makes it all the worse when you give up on static proofs in places where they are possible.

steveklabnik 6 days ago

That’s correct, and “valid but unspecified state” is possible/common too.