Как избежать ошибок строгого алиасинга при использованииalign_storage

Я использую 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 таким образом нарушает строгое сглаживание, как мне его использовать?
  • Is there actually a strict-aliasing problem in get() given that there are no other pointer based operations in the function?
    • What about if get() is inlined?
    • А как насчет get() = 4; get() = 3.2? Может ли эта последовательность быть переупорядочена из-за того, что int и float являются разными типами?

person marack    schedule 07.10.2013    source источник
comment
выравнивание как-то влияет на проблему алиасинга?! ожидайте, что это часть причины (но это совсем не меняет того, что стандарт запрещает это, не так ли?)   -  person dhein    schedule 07.10.2013
comment
@Zaibis Нет, использование aligned_storage в отличие от любого другого типа в качестве буфера не должно иметь никакого значения. Причина, по которой я указал здесь aligned_storage, заключается в том, что он, кажется, был разработан с учетом такого рода использования.   -  person marack    schedule 08.10.2013
comment
@marack: вы не должны (и действительно не можете) использовать aligned_storage напрямую. Я отредактировал свой ответ, чтобы попытаться сделать это (даже) более ясным.   -  person rici    schedule 08.10.2013
comment
Я просто больше спрашивал о выравнивании псевдонимов, поскольку вы пометили это как строгое псевдонимы, и я не понимаю, при чем тут это.   -  person dhein    schedule 08.10.2013


Ответы (1)


std::aligned_storage является частью <type_traits>; как и большинство остальных обитателей этого заголовочного файла, он является просто держателем для некоторых определений типов и не предназначен для использования в качестве типа данных. Его работа состоит в том, чтобы взять размер и выравнивание и сделать вас типом 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)...);
}
person rici    schedule 07.10.2013
comment
Ах спасибо. Я не осознавал, что aligned_storage был трейтом, раскрывающим тип, а не сам тип. Как вы упомянули, использование aligned_storage<...>::type правильно решает проблему строгого псевдонима. - person marack; 08.10.2013
comment
В этом случае другой тип является псевдонимом символьного типа. Правило strict aliasing не является симметричным, оно запрещает псевдонимы другого типа, но разрешает псевдонимы другого типа (чего здесь не происходит). Однако в этот ответ утверждается, что aligned_storage не нарушает строгого псевдонима по другим причинам (хотя я не совсем следуйте его аргументам). - person M.M; 12.03.2015