Размещение нового в 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 new
s, гарантируя, что вызов переходит на глобальный.
Вместе это гарантирует, что вызов поступит в библиотеку размещения 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
обойти любые классовые функции распределения.