Consider this snippet of C++23 code:
struct MyData {
std::int32_t value;
};
std::vector<char> read_from_net();
void send_over_net(std::vector<char>&);
...
std::vector<char> buffer = read_from_net();
std::start_lifetime_as<MyData>(buffer.data())->value = 222; // (1)
send_over_net(buffer); // (2)
Assuming all requirements to start lifetime of MyData
at buffer.data()
are respected:
(2)
without UB
considering that start_lifetime_as
ended previous object's
lifetime and started new one's?start_lifetime_as
just
start another lifetime but also preserves previous one?std::launder(buffer.data())[0]
to access original buffer as
char array after (2)
?TL;DR It's undefined behavior.
Kudos for thinking about this.
The key points are that
reusing the storage to create a new object does end the lifetime of old objects1
aliasing through char
is allowed
https://eel.is/c++draft/basic.lval#11.3
starting lifetime of a new object of different type ends validity of all pointers into any part of that object
You can get a new, valid pointer through std::launder
of an existing expired pointer, or through reinterpret_cast
ing of a valid pointer to the new object (the pointer returned from std::start_lifetime_as
).
With std::launder
, the returned pointer is valid, but it doesn't restore validity of other expired pointers (such as the ones inside std::vector
itself). Ouch.
1 The one rule that might be interpreted to permit this shows up here, in the relatively-new language around
reused by an object that is not nested within
If the new object is "nested within" the old objects owned by std::array
, then their lifetime continues making everything ok.
https://eel.is/c++draft/intro.object#4
But that permission applies only to unsigned char
and std::byte
, not plain char
, so it doesn't help here.