C++ Logo

std-proposals

Advanced search

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

From: Tiago Freire <tmiguelf_at_[hidden]>
Date: Wed, 17 Dec 2025 08:35:42 +0000
It is physically impossible for 2 distinct object to share the same address at the same time unless they have 0 size (i.e. unless there is no object, at which point address is irrelevant).
You can have 2 distinct pointers to the same address, pointing to the same object, but where the pointers can misrepresent the type of the object.

An object is an abstract representation of a "thing", while the "pointer type" is just a hint of what that "thing is".
I can have a "chair" and place a label on it calling a "table", I can even put a vase on top of this thing that I've labeled "table", I can invite guests to have dinner using this "table", but it is still a chair, and you certainly don't have both a distinct "table" and a "chair".


-----Original Message-----
From: Std-Proposals <std-proposals-bounces_at_lists.isocpp.org> On Behalf Of Halalaluyafail3 via Std-Proposals
Sent: Wednesday, December 17, 2025 02:40
To: Halalaluyafail3 via Std-Proposals <std-proposals_at_lists.isocpp.org>
Cc: Halalaluyafail3 <luigighiron_at_gmail.com>
Subject: [std-proposals] std::launder when multiple objects of the same type exist at the same address

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.

Given this, it is unclear what std::launder((S*)s.x) points to. (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.

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.
--
Std-Proposals mailing list
Std-Proposals_at_lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2025-12-17 08:35:46