In C++26, reading uninitialized variables is no longer undefined, it's "erroneous" now (What is erroneous behavior? How is it different from undefined behavior?).
However, the wording for this confuses me:
otherwise, the bytes have erroneous values, where each value is determined by the implementation independently of the state of the program.
(bold mine)
To me, this reads like the implementation must overwrite the values with something (e.g. 0xBEBEBEBE
), because leaving them truly uninitialized might make them dependent on the "state of the program", contradicting the bold part.
Is my interpretation correct? Are implementations forced to overwrite uninitialized variables now?
Best Answer
The linked P2795R5 says under Performance and security implications:
It also points out that although automatic locals can be annotated
[[indeterminate]]
to suppress this initialization, there's no way to avoid it for any temporaries.So it seems like your interpretation is correct.
Oddly, it doesn't seem important what this magic value is - or even whether this initialization really happens - except that it can't be a trap pattern. As already pointed out there's no magic value of a byte that is unambiguously erroneous at runtime and still safe to load, copy, and compare.
Edit - why do I say it doesn't seem to matter what the magic value is, or even whether this initialization really happens?
The motivation is to stop evaluation (ie. glvalue-to-prvalue conversion) of uninitialized automatic variables being Undefined Behaviour. Instead it will be Erroneous Behaviour which implementations are encouraged to diagnose.
The above can't be contingent on a specific bit pattern if that bit pattern could ever be produced by a valid expression, without the risk of misfiring diagnostics.
No usual primitives have such magic bit patterns, except for the now-uncommon trap representation.
eg. you couldn't use either quiet or signalling
NaN
to mark erroneous values, because ifneeds to treat both values differently, it can't be based on the bit pattern.
The same is trivially true for integer types, and anyway [basic.indet/2] says
where all the exclusions are related to "unsigned ordinary character type" and
std::byte
, so in:foo
with the non-erroneous value must not be diagnosedfoo
with exactly the same bit-pattern may be diagnosedIf the only goal is to prevent evaluation of uninitialized (automatic) variables escaping to UB, it's sufficient to require this kind of initialization only for types with trap representations.
It may also be required to disable (or guard with diagnostic checks) some optimizations previously allowed by UB, but it's neither necessary nor sufficient for that to depend on a specific bit pattern.