Тип punning, char[] и разыменование
У меня есть структура, которая предназначена для хранения пользовательских данных (т. е. из плагина). Он имеет такой char[]
с заданным максимальным размером для хранения этих данных.
struct A
{
// other members omitted
// data meant to be type punned, only contains PODs
char data[256];
};
Затем есть пример структуры пользователя, которая имеет статическую функцию для приведения себя из A
.
struct B
{
int i;
double d;
static B& FromA_ref(A& a)
{
// static_assert that sizeof(B) < sizeof(A::data)
return * reinterpret_cast<B*>(a.data);
}
};
Я компилирую с g++ -O3 -std=c++0x -Wall -o test test.cpp
(GCC 4.6.1).
Это вызывает предупреждение dereferencing type-punned pointer will break strict-aliasing rules
. Я думал, что это будет нормально, так как я использовал char[]
в качестве хранилища, которое, как я думал, будет следовать тем же правилам, что и char*
. Я нахожу его странно, что этого не происходит. Что ж,... Я не могу изменить это прямо сейчас, так что давайте двигаться дальше.
Теперь рассмотрим следующий метод:
struct B
{
....
static B* FromA_ptr(A& a)
{
// static_assert that sizeof(B) < sizeof(A::data)
return reinterpret_cast<B*>(a.data);
}
}
Поскольку я ничего не разыменовываю здесь, GCC не выводит никакого предупреждения. То же самое происходит, когда я использую свой указатель на B
позже.
A a;
auto b = B::FromA_ptr(a);
b->i = 2; // no warnings.
Но безопасно ли это делать ?У меня такое чувство, что я стараюсь обойти проблему, а не решить ее. Для меня ->
все еще разыменовывает переменную как-то.
Кроме того, есть ли лучший способ достичь эффекта ? Т. е. получить модифицируемую ссылку (или указатель), приведенную из хранилища внутри другой структуры ? (Union не будет работать, так как набор хранимых типов не известен, когда A
определен, и некоторые могут быть добавлены через плагины, memcpy
заставит меня копировать данные туда и обратно, хотя это, кажется, единственный безопасный способ до сих пор)
1 ответ:
Ответ-Нет, это небезопасно (см. Этот так что вопрос)
GCC будет считать, что указатели не могут иметь псевдонимов. Например, если вы назначаете через один, а затем читаете из другого, GCC может, в качестве оптимизации, изменить порядок чтения и записи - я видел, как это происходит в производственном коде, это не очень приятно отлаживать.
Атрибут((may_alias )) по используемым типам, вероятно, ближе всего вы можете получить к отключению предположения для конкретного раздел кода.