я использую std::aligned_storage
в качестве резервного хранилища для варианта шаблона. Проблема в том, как только я включу -O2
на gcc я начинаю получать предупреждения о том, что «разыменование указателя типа-наказанного нарушит строгий псевдоним».
Реальный шаблон намного сложнее (тип проверяется во время выполнения), но минимальный пример для генерации предупреждения:
struct foo
{
std::aligned_storage<1024> data;
// ... set() uses placement new, stores type information etc ...
template <class T>
T& get()
{
return reinterpret_cast<T&>(data); // warning: breaks strict aliasing rules
}
};
я точно уверен boost::variant
по сути дела делает то же самое, что и это, но я не могу понять, как они избегают этой проблемы.
Мои вопросы:
aligned_storage
таким образом нарушает строгие псевдонимы, как я должен его использовать?get()
учитывая, что в функции нет других операций с указателями?
get()
встраивается?get() = 4; get() = 3.2
? Может ли эта последовательность быть переупорядочена из-за int
а также float
быть разными типами?std::aligned_storage
это часть <type_traits>
; как и большинство остальных обитателей этого заголовочного файла, он просто является держателем для некоторых typedef и не предназначен для использования в качестве типа данных. Его работа заключается в определении размера и выравнивания и создании типа POD с этими характеристиками.
Вы не можете использовать std::aligned_storage<Len, Align>
непосредственно. Вы должны использовать std::aligned_storage<Len, Align>::type
преобразованный тип, который представляет собой «тип POD», подходящий для использования в качестве неинициализированного хранилища для любого объекта, размер которого не превышает Len
и чье выравнивание является делителем Align
. «(Align
по умолчанию наибольшее полезное выравнивание больше или равно Len
.)
Как отмечает стандарт C ++, обычно тип, возвращаемый std::aligned_storage
будет массив (указанного размера) unsigned char
с указателем выравнивания. Это позволяет избежать правила «отсутствия строгого псевдонима», поскольку тип символа может иметь псевдоним любого другого типа.
Так что вы можете сделать что-то вроде:
template<typename T>
using raw_memory = typename std::aligned_storage<sizeof(T),
std::alignment_of<T>::value>::type;
template<typename T>
void* allocate() { return static_cast<void*>(new raw_memory<T>); }
template<typename T, typename ...Arg>
T* maker(Arg&&...arg) {
return new(allocate<T>()) T(std::forward<Arg>(arg)...);
}
Других решений пока нет …