C++ Logo

std-proposals

Advanced search

Re: [std-proposals] std::launder when multiple objects of the same type exist at the same address

From: Jonathan Wakely <cxx_at_[hidden]>
Date: Wed, 17 Dec 2025 09:22:40 +0000
On Wed, 17 Dec 2025, 01:42 Halalaluyafail3 via Std-Proposals, <
std-proposals_at_[hidden]> wrote:

> N5032 Section 17.6.5 [ptr.launder] Paragraphs 2 and 3:
> > Preconditions: p represents the address A of a byte in memory. An object
> X
> > that is within its lifetime (6.8.4) and whose type is similar (7.3.6) to
> T is
> > located at the address A. All bytes of storage that would be reachable
> through
> > (6.9.4) the result are reachable through p.
> >
> > Returns: A value of type T* that points to X.
>
> This section seems to assume that at most one possible X can exist.
> However, it
> appears possible to construct two objects in a way that they could both be
> that
> X:
>
> struct S{
> unsigned char x[4];
> };
> static_assert(sizeof(S)==4);
> S s;
> S*p=::new((void*)s.x)S;
> std::launder((S*)s.x)->~S();
> s.~S();
>
> All pointers involved here can reach all 4 bytes of s, so reachability
> doesn't
> affect anything. s is an object of type S which exists at some address A.
> An
> array s.x spanning all of the bytes of s exists as a subobject of s. s.x
> must
> have the same address as s because it occupies the same storage, and
> because of
> pointer-interconvertibility. Then, another object of type S is created in
> s.x
> where s.x provides storage for it. This new object *p occupies the same
> storage
> as s.x, so it must have the same address as s.x and s. So there are two
> objects
> s and *p which both have the same address, same type, same reachable
> bytes, and
> are both within their lifetime.
>

Yes, unfortunately.


> Given this, it is unclear what std::launder((S*)s.x) points to.


Who cares? launder is mostly useless, and your example is only possible for
empty types, which can't contain const members that would make launder
"useful".


(S*)s.x points
> to the same address as both s and *p, and both objects meet the
> requirements to
> be X. Here is an example that demonstrates each case individually:
>
> //same S as before
> alignas(S)unsigned char a[4];
> ::new((void*)a)S;
> std::launder((S*)a)->~S();
> S b;
> std::launder((S*)b.x)->~S();
>
> std::launder((S*)s.x) could be either case, so is s.~S() defined (launder
> results in p) or undefined (launder results in &s)?
>
> See also GCC bug 121180, this issue can arise from types such as std::any
> which
> store objects internally if they are small enough. Specifically, an empty
> class
> can be stored in a std::any object and the std::any object can overlap with
> another object that has the same empty class type just right to cause both
> empty
> classes to have the same address.
>

And Tomasz said there that we need an LWG issue to clarify that types like
std::any (and some other types) should have wording to say this can happen,
but we can't (and probably don't want to) prevent it from happening.


> I'm thinking about making a LWG issue for this. Though I don't think any
> implementations implement these incorrectly because this overlapping
> requires
> the storage to be unused and cannot happen in constant expressions.
>

I don't think LWG can possibly do anything about this, the solution is not
to play these silly games with std::launder when you can have an object
nested within the storage of another object of the same type. It's a
self-inflicted problem.

Received on 2025-12-17 09:22:58