Search code examples
c++language-lawyerc++20placement-new

How can I access a second disparate object in the same over-sized allocated given only a pointer to the first?


Consider the following.

struct T { int a = 1; };
struct U { char b = 2; };
static_assert(alignof(U) == 1);

// Allocate storage.  Cast via P0593 to access implicit array of unsigned char.
auto* s = reinterpret_cast<unsigned char*>(::operator new(sizeof(T) + sizeof(U)));

// Place T and U in that storage.
T* t = new (s) T;
U* u = new (s + sizeof(T)) U;

// Is there a legal way to get a pointer to `u` given only `t` and not `s`?
U* u2 = reinterpret_cast<U*>(reinterpret_cast<unsigned char*>(t) + sizeof(T));

The question: Is there a way, given only t to get u?

I know I can std::launder() s to get t, and presumably I can std::launder() s + sizeof(T) to get u. But, if all I have is t, is there a way?

I know that std::launder() has requirements about "reachability", so I don't think it can be part of the solution since u is not reachable from t.

I know that if I put T and U together in a struct, things are different, but that's not the case I am interested in. (Consider that U might actually be of dynamic size.)

I think my question boils down to: How do I recover s (the underlying complete storage) from t? I know I can get the object representation of the object at t, but I don't think that's the same as s because it's only sizeof(T) long, right?

If I want to allocate once and store these non-array disparate objects, am I forced to store a pointer to u inside t just to recover it later?


Solution

  • As you are saying std::launder has a precondition that explicit prohibits this. It seems intended to be impossible.

    There are a few other constructs in the language that can make more bytes reachable than should be possible by this precondition, see e.g. my questions std::launder vs placement-new reachability condition and Does implicit object creation circumvent reachability conditions of std::launder?.

    However, given that these restrictions were explicitly added to std::launder, I suspect that them not being applied to these other constructs as well is the defect and not the other way around.


    I do not know whether there is any compiler that actually makes use of this reachability restriction for optimization though.