ABICT your use is safe.
- Placement new of an object of type T will create an object starting at the address passed in.
§5.3.4/10 says:
A new-expression passes the amount of space requested to the
allocation function as the first argument of type std::size_t. That
argument shall be no less than the size of the object being created;
it may be greater than the size of the object being created only if
the object is an array.
For a non-array object, the size allocated cannot be greater that the size of the object, so the object representation must start at the beginning of the allocated memory in order to fit.
Placement new returns the pointer passed in (see § 18.6.1.3/2) as the result of the "allocation", so the object representation of the constructed object will start at that address.
static_cast<>
and implicit conversions between T*
type and void*
convert between a pointer to the object and a pointer to its storage, if the object is a complete object.
§4.10/2 says:
A prvalue of type “pointer to cv T,” where T is an object type, can be
converted to a prvalue of type “pointer to cv void”. The result of
converting a “pointer to cv T” to a “pointer to cv void” points to the
start of the storage location where the object of type T resides, as
if the object is a most derived object (1.8) of type T [...]
This defines the implicit conversion to convert as stated. Further §5.2.9[expr.static.cast]/4 defines static_cast<>
for explicit conversions, where an implicit conversion exists to have the same effect as the implicit conversion:
Otherwise, an expression e
can be explicitly converted to a type T
using a static_cast
of the form static_cast<T>(e)
if the declaration
T t(e);
is well-formed, for some invented temporary variable t
(8.5).
The effect of such an explicit conversion is the same as performing
the declaration and initialization and then using the temporary
variable as the result of the conversion. [...]
For the inverse static_cast<>
(from void*
to T*
), §5.2.9/13 states:
A prvalue of type “pointer to cv1 void” can be converted to a prvalue
of type “pointer to cv2 T,” where T is an object type and cv2 is the
same cv-qualification as, or greater cv-qualification than, cv1. [...]
A value of type pointer to object converted to “pointer to cv void”
and back, possibly with different cv-qualification, shall have its
original value.
So if you have a void*
pointing to the storage of the T
object (which is the pointer value that would result from the implicit conversion of a T*
to the object, then a static_cast
of that to a T*
will produce a valid pointer to the object.
Returning to your question, the preceding points imply that if you have
typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;
void * pvt_ = &t_;
T* pT = new (&t_) T(args...);
void * pvT = pT;
then
- the storage of
*pT
exactly overlays the first size(T) bytes of the storage of t_
, so that pvT == pvt_
pvt_ == static_cast<void*>(&t_)
static_cast<T*>(pvT) == pT
- Taken together that yields
static_cast<T*>(static_cast<void*>(&t_)) == pT
new
. The obvious case is allocating vianew[]
; Implementations often store information needed to destruct the array's objects in the leading bytes of the allocation. – Theodicystd::optional
in C++17... – Yoon