Vittorio Romeo
Vittorio Romeo

Reputation: 93364

Using placement `new`'s result on a object with a reference member

The "Using placement new to update a reference member?" question shows this example (simplified):

struct Foo { int& v_; };

int a, b;
Foo f{a};

new (&f) Foo{b};

assert(&f.v_ == &a); // UB

Accessing f through its original name is definitely UB as explained by T.C. in the linked question. I know that std::launder can be used to work around this:

assert(&std::launder(&f)->v_ == &a); // OK, will fire the assert

But what about using the pointer returned by placement new? I.e.

auto p = new (&f) Foo{b};    
assert(&(p->v_) == &a); // UB? OK?

In this case, we're not referring to the object through its original name, but through whatever placement new returned.

Is this undefined behavior or allowed by the Standard?

Upvotes: 3

Views: 149

Answers (1)

Barry
Barry

Reputation: 303537

This:

auto p = new (&f) Foo{b};    
assert(&(p->v_) == &a); // UB? OK?

Is well-defined. The assert will trigger. new creates a new object, and p points to that new object. That is all perfectly fine. We're reusing f's storage, and there are a lot of rules in [basic.life] about what is OK and not OK about how you can use old names - there are rules about how you can use f, previous pointers to f, etc. You cannot reuse &f without launder-ing it. There rules about how and when you can call destructors in this case, or what about reusing storage for static storage or const objects - none of which matter here either.

But p is a new thing - it just refers to the new object. And p->v_ is the new int& that you created that refers to b. That is not the same object as a, so the pointers compare unequal.

Upvotes: 5

Related Questions