31

Suppose I have a type template parameter T.

And suppose I have a std::aligned_storage as follows:

typename std::aligned_storage<sizeof(T), alignof(T)>::type storage;

I want to placement new a T into the storage.

What is the standard-compliant pointer value/type to pass to the placement new operator, and how do I derive that from storage?

new (& ???) T(a,b,c);

For example:

new (&storage) T(a,b,c);
new (static_cast<void*>(&storage)) T(a,b,c);
new (reinterpret_cast<T*>(&storage)) T(a,b,c);
new (static_cast<T*>(static_cast<void*>(&storage));

Which of the above (if any) are compliant, and if none, what is the better way?

Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319

2 Answers2

55

The most paranoid way is

::new ((void *)::std::addressof(storage)) T(a, b, c);

Explanation:

  • ::std::addressof guards against overloaded unary operator& on storage, which is technically allowed by the standard. (Though no sane implementation would do it.) The ::std guards against any non-top-level namespaces (or classes) called std that might be in scope.
  • (void *) (which in this case is the equivalent of a static_cast) ensures that you call the placement operator new taking a void * rather than something else like decltype(storage) *.
  • ::new skips any class-specific placement operator news, ensuring that the call goes to the global one.

Together, this guarantees that the call goes to the library placement operator new taking a void *, and that the T is constructed at where storage is.

In most sane programs, though,

new (&storage) T(a,b,c);

should be sufficient.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • 14
    OK, +1 for the sheer level of paranoia. If I ever embark on writing a Hell++ implementation, I'll ask you to collaborate :-) – Angew is no longer proud of SO Jan 28 '15 at 12:43
  • @Nikos See https://stackoverflow.com/a/332086/1896169 . "[C-style casts] are defined as the first of the following which succeeds: `const_cast`, `static_cast` (though ignoring access restrictions), `static_cast` (see above) then `const_cast`, `reinterpret_cast`, `reinterpret_cast` then `const_cast`" – Justin Jul 18 '19 at 17:55
5

The placement allocation function is described as follows (C++14 n4140 18.6.1.3):

void* operator new(std::size_t size, void* ptr) noexcept;

Returns: ptr.

Remarks: Intentionally performs no other action.

20.10.7.6 table 57 describes aligned_storage<Len, Align> thus:

The member typedef type shall be a POD type suitable for use as uninitialized storage for any object whose size is at most Len and whose alignment is a divisor of Align.

This implies that in your case, &storage is suitably aligned for holding an object of type T. Therefore, under normal circumstances1, all 4 ways you've listed of calling placement new are valid and equivalent. I would use the first one (new (&storage)) for brevity.


1 T.C. correctly pointed out in the comments that it is technically possible for your program to declare an overload of the allocation function taking a typename std::aligned_storage<sizeof(T), alignof(T)>::type*, which would then be selected by overload resolution instead of the library-provided 'placement new' version.

I would say this unlikely in at least 99.999% of cases, but if you need to guard against that as well, use one of the casts to void*. The direct static_cast<void*>(&storage) is enough.

Also, if you're paranoid to this level, you should probably use ::new instead of just new to bypass any class-specific allocation functions.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455