The following code example is from cppreference on std::launder:
alignas(Y) std::byte s[sizeof(Y)];
Y* q = new(&s) Y{2};
const int f = reinterpret_cast<Y*>(&s)->z; // Class member access is undefined behavior
It seems to me that third line will result in undefined behaviour because of [basic.life]/6 in the standard:
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated ... The program has undefined behavior if ... the pointer is used to access a non-static data member or call a non-static member function of the object.
Why didn't placement new start the lifetime of object Y?
The placement-new did start the lifetime of the
Yobject (and its subobjects).But object lifetime is not what
std::launderis about.std::laundercan't be used to start the lifetime of objects.std::launderis used when you have a pointer which points to an object of a type different than the pointer's type, which happens when youreinterpret_casta pointer to a type for which there doesn't exist an object of the target type which is pointer-interconvertible with the former object.std::laundercan then (assuming its preconditions are met) be used to obtain a pointer to an object of the pointer's type (which must already be in its lifetime) located at the address to which the pointer refers.Here
&sis a pointer pointing to an array ofsizeof(Y)std::bytes. There is also an explicitly createdYobject sharing the address with that array and the array provides storage for theYobject. However, an array (or array element) is not pointer-interconvertible with an object for which it provides storage. Therefore the result ofreinterpret_cast<Y*>(&s)will not point to theYobject, but will remain pointing to the array.Accessing a member has undefined behavior if the glvalue used doesn't actually refer to an object (similar) to the glvalue's type, which is here the case as the lvalue refers to the array, not the
Yobject.So, to get a pointer and lvalue to the
Yobject located at the same address as&sand already in its lifetime, you need to callstd::launderfirst:All of this complication can of course be avoided by just using the pointer returned by
newdirectly. It already points to the newly-created object: