So I was reading P2590R2 which introduces std::start_lifetime_as
, and one of the sections is making me question something that I had previously thought was defined behaviour, so I was hoping someone might be able to clarify.
If I have some code like this:
struct WString_header
{
WString_header(std::wstring_view str)
: m_size(str.size())
{
// this shouldn't be undefined behaviour so long as
// the storage we're being allocated in (i.e. an array of bytes)
// can actually fit this array?
wchar_t *chars = new (this + 1) wchar_t[str.size()];
std::copy(str.begin(), str.end(), chars);
}
size_t size() const
{
return m_size;
}
const wchar_t *chars() const
{
//undefined behaviour?
return reinterpret_cast<const wchar_t *>(this + 1);
}
protected:
size_t m_size;
};
I previously thought that retrieving the chars via using reinterpret_cast
wouldn't be undefined behaviour as we had previously allocated an object of that type at that address in the constructor, however in the linked paper, on their section explaining the difference between start_lifetime_as
and launder
, they say:
On the other hand, std::launder never creates a new object, but
can only be used to obtain a pointer to an object that already exists at the given memory location,
with its lifetime already started through other means.
The fact that std::launder
is being referred to with this wording makes me think I might have an incorrect model of object lifetime.
As I previously understood it once you allocate an object at a given location in memory, accessing it through reinterpret_cast would always be defined behaviour no matter how or where the pointer being cast was obtained as that memory address does contain an instance of that that type (obviously minding issues like arrays where simply calculating an address outside the array other than the past-the-end pointer is undefined behaviour).
However, given that description of std::launder
in the paper, it makes me think that that model should be amended that any attempt to dereference a pointer obtained via reinterpret_cast
should instead first be passed through launder to prevent undefined behaviour (unless the type is char or similar due to the unique aliasing rules for those types), however this then brings up the question of why reinterpret_cast
would ever be useful for converting pointers if the only thing that could be done with the result would be pointer arithmetic and comparison with pointers of the new type.
So have I just been lucky with compiler optimisations of my code thus far, or am I getting confused by the wording in the paper?