Reputation: 93364
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
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