Размещение нового в std::align_storage?

Предположим, у меня есть параметр шаблона типа T.

И предположим, у меня есть std::aligned_storage следующее:

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

Я хочу разместить новый Т в storage,

Что такое стандартно-совместимое значение / тип указателя для передачи новому оператору размещения, и как я могу получить это из storage?

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

Например:

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));

Какие из вышеперечисленных (если таковые имеются) являются совместимыми, и если нет, что является лучшим способом?

2 ответа

Решение

Самый параноидальный способ

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

Объяснение:

  • ::std::addressof предохраняет от перегруженных одинарных operator& на storage, что технически разрешено стандартом. (Хотя никакая вменяемая реализация не сделала бы это.) ::std защищает от любых пространств имен (или классов) не верхнего уровня, называемых std это может быть в объеме.
  • (void *) (который в этом случае является эквивалентом static_cast) гарантирует, что вы позвоните в место размещения operator new принимая void * а не что-то подобное decltype(storage) *,
  • ::new пропускает любое классно-ориентированное размещение operator news, гарантируя, что вызов переходит на глобальный.

Вместе это гарантирует, что вызов поступит в библиотеку размещения operator new принимая void *и что T построен там, где storage является.

В большинстве вменяемых программ, тем не менее,

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

должно быть достаточно.

Функция размещения размещения описывается следующим образом (C++14 n4140 18.6.1.3):

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

Возвращает: ptr,

Примечания: Преднамеренно не выполняет никаких других действий.

20.10.7.6 Таблица 57 описывает aligned_storage<Len, Align> таким образом:

Член typedef type должен быть типом POD, подходящим для использования в качестве неинициализированного хранилища для любого объекта, размер которого не превышает Len и выравнивание которого является делителем Align.

Это подразумевает, что в вашем случае &storage выровнен для удержания объекта типа T, Таким образом, при нормальных обстоятельствах1, все 4 способа, которые вы перечислили для размещения вызовов new действительны и эквивалентны. Я бы использовал первый (new (&storage)) для краткости.


1 TC правильно указал в комментариях, что технически возможно, что ваша программа объявляет перегрузку функции распределения, принимая typename std::aligned_storage<sizeof(T), alignof(T)>::type*, который затем будет выбран по разрешению перегрузки вместо предоставленной библиотекой версии "размещение нового".

Я бы сказал, что это маловероятно, по крайней мере, в 99,999% случаев, но если вам нужно также принять меры против этого, используйте одно из приведений, чтобы void*, Прямой static_cast<void*>(&storage) достаточно.

Кроме того, если вы параноик до этого уровня, вы должны, вероятно, использовать ::new вместо просто new обойти любые классовые функции распределения.

Другие вопросы по тегам