Это действительно нарушает правила строгого наложения имен?

Когда я компилирую этот пример кода с использованием g++, я получаю это предупреждение:

предупреждение: разыменование указателя типа-наказанного нарушит правила строгого наложения имен [-Wstrict-aliasing]

Код:

#include <iostream>

int main() 
{
   alignas(int) char data[sizeof(int)];
   int *myInt = new (data) int;
   *myInt = 34;

   std::cout << *reinterpret_cast<int*>(data);
}

В этом случае не data псевдоним int, и, следовательно, приведение его обратно к int не будет нарушать строгие правила псевдонимов? Или я что-то здесь упускаю?

Редактировать: странно, когда я определяю data как это:

alignas(int) char* data = new char[sizeof(int)];

Предупреждение компилятора исчезает. Распределение стека имеет значение со строгим псевдонимом? Имеет ли тот факт, что это char[] и не char* значит, это не может на самом деле псевдоним любого типа?

2 ответа

Решение

Предупреждение абсолютно оправдано. Разрушенный указатель на data не указывает на объект типа int и кастинг не меняет этого. Смотрите [basic.life] / 7:

Если по истечении времени жизни объекта и до повторного использования или освобождения хранилища, которое занимал объект, в месте хранения, которое занимал исходный объект, создается новый объект, указатель, указывающий на исходный объект, ссылка, которая ссылка на исходный объект, или имя исходного объекта будет автоматически ссылаться на новый объект и, как только начнется время жизни нового объекта, может использоваться для управления новым объектом, если:
(7.1) - [..]
(7.2) - новый объект того же типа, что и исходный объект (без учета cv-квалификаторов верхнего уровня),

Новый объект не является массивом char, но int, P0137, который формализует понятие указания, добавляет launder:

[ Примечание: если эти условия не выполняются, указатель на новый объект можно получить из указателя, который представляет адрес его хранилища, вызвав std::launder (18.6 [support.dynamic]). - конец примечания ]

Т.е. ваш фрагмент может быть исправлен таким образом:

std::cout << *std::launder(reinterpret_cast<int*>(data));

.. или просто инициализировать новый указатель из результата размещения new, что также удаляет предупреждение.

*myInt = 34; это выражение правильно сформировано, потому что data предоставить хранилище для объекта типа int и myIntуказатель на объект типа int. Итак, разыменование такого указателя может получить доступ к объекту типа int.

За *reinterpret_cast<int*>(data);это выражение нарушит строгий псевдоним указателя. Во-первых, это преобразование массива в указатель, которое применяется кdata, Результатом будет указатель на начальный элемент data, это означает операнд reinterpret_cast<int*> указатель на предмет data.
По следующему правилу:

Если объект создается в хранилище, связанном с подобъектом члена или элементом массива e, созданный объект является подобъектом объекта, содержащего e, если:

  • время жизни объекта, содержащего e, началось и не закончилось, и
  • хранилище для нового объекта точно перекрывает место хранения, связанное с e, и
  • новый объект имеет тот же тип, что и e (без учета cv-квалификации).

Объект типа int не удовлетворяет ни одному из этих правил. Следовательно, операндreinterpret_cast<int*>не является указателем на объект, который может преобразовывать указатель с объектом типа int. Итак, результатreinterpret_cast<int*> не является указателем на объект типа int.

программа пытается получить доступ к сохраненному значению объекта с помощью glvalue, отличного от одного из следующих типов, поведение не определено:

  • динамический тип объекта.
  • [...]

Как насчет изменения

std::cout << *reinterpret_cast<int*>(data);

в

int *tmp   = reinterpret_cast<int*>(data);
std::cout << *tmp;

?

In my case it got rid of the warning.

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