Question

In C++26, are implementations required to "initialize" uninitialized variables to some fixed byte pattern?

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:

[basic.indet]/1.2

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?

 5  129  5
1 Jan 1970

Solution

 2

The linked P2795R5 says under Performance and security implications:

  • The automatic storage for an automatic variable is always fully initialized, which has potential performance implications. P2723R1 discusses the costs in some detail. Note that this cost even applies when a class-type variable is constructed that has no padding and whose default constructor initializes all members.
  • In particular, unions are fully initialized. ...

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.

2024-07-25
Useless

Solution

 2

It depends on what you mean by "some fixed byte pattern".

Implementations can choose values that are dependent on the type of the variable, because that's a static property of the program, not dependent on the program's state. In cases where you declare e.g. an aligned buffer of unsigned char or std::byte and then later placement new objects into them, note that it is the first step, obtaining the storage, that writes erroneous values into the memory; the placement new step will be a no-op if it performs a trivial default initialization. But the compiler could choose to do some static analysis to see what type you are going to provide storage for using the buffer, and choose the erroneous values accordingly.

It's also theoretically possible that an implementation could choose the initial erroneous values using a true random number generator, but obviously the performance of that would be terrible, so it's unlikely for an actual implementation to do that.

Note that initial erroneous values do not apply to objects with static or thread storage duration (which are zero-initialized) or dynamic storage duration. It's also possible to opt out using the [[indeterminate]] attribute.

2024-07-25
Brian Bi