2015-01-28 3 views
17

Предположим, я есть шаблон типа параметра T.Размещение новых в std :: aligned_storage?

И предположим, у меня есть 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)); 

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

ответ

30

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

::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, гарантируя, что вызов переходит к глобальному.

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

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

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

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

+7

ОК, +1 для чистого уровня паранойи. Если я когда-либо приступаю к написанию реализации Hell ++, я попрошу вас сотрудничать :-) – Angew

4

Функция распределения размещения описывается следующим образом (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> таким образом:

Член ЬурейеГо type должен быть типом СТРУЧКА подходит для использования в качестве неинициализированного хранения для любого объекта , размер которого в большинстве Лен и чье выравнивание является делителем Выравнивание.

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


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

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

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

+1

Только версия с 'void *' литой гарантированно будет идти в место размещения библиотеки. –

+0

Кроме того, чтобы быть дополнительным параноидальным, ':: new'. –