c++language-lawyerundefined-behaviorlifetimetype-punning

Does start_lifetime_as forbid access to original buffer?


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:

  1. Is there any UB in this code? Is starting lifetime like this and modifying field well defined?
  2. Can I access original buffer as chars at (2) without UB considering that start_lifetime_as ended previous object's lifetime and started new one's?
  3. Or does start_lifetime_as just start another lifetime but also preserves previous one?
  4. Should I use std::launder(buffer.data())[0] to access original buffer as char array after (2)?

Solution

  • TL;DR It's undefined behavior.


    Kudos for thinking about this.

    The key points are that

    1. reusing the storage to create a new object does end the lifetime of old objects1

      https://eel.is/c++draft/basic.life#2.5

    2. aliasing through char is allowed

      https://eel.is/c++draft/basic.lval#11.3

      • but you have to have a validly derived pointer, which you don't, because
    3. starting lifetime of a new object of different type ends validity of all pointers into any part of that object

      https://eel.is/c++draft/basic.life#10

    4. 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.

        https://eel.is/c++draft/ptr.launder#5


    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.

    https://eel.is/c++draft/intro.object#3