Вы уже получили правильный ответ, и именно нарушение строгого правила псевдонимов приводит к непредсказуемому поведению кода.Я просто хотел бы отметить, что заголовок вашего вопроса ссылается на «приведение указателя на исходный класс».На самом деле ваш код не имеет ничего общего с приведением «назад».Ваш код выполняет реинтерпретацию необработанного содержимого памяти, занятого указателем void *
в качестве указателя A *
.Это не «отбрасывание».Это реинтерпретация .Даже отдаленно одно и то же.
Хороший способ проиллюстрировать разницу - использовать примеры int
и float
.Значение float
, объявленное и инициализированное как
float f = 2.0;
, может быть приведено (явно или неявно преобразовано ) к int
типу
int i = (int) f;
с ожидаемымрезультат
assert(i == 2);
Это действительно приведение (преобразование).
В качестве альтернативы, то же значение float
можно также интерпретировать как значение int
int i = (int &) f;
Однако в этом случае значение i
будет совершенно бессмысленным и обычнонепредсказуемы.Я надеюсь, что из этих примеров легко увидеть разницу между преобразованием и реинтерпретацией памяти.
Реинтерпретация - это именно то, что вы делаете в своем коде.Выражение (A *&) p
представляет собой не что иное, как реинтерпретацию необработанной памяти, занятой указателем void *p
в качестве указателя типа A *
.Язык не гарантирует, что эти два типа указателей имеют одинаковое представление и даже одинаковый размер.Таким образом, ожидание предсказуемого поведения в вашем коде похоже на ожидание, что приведенное выше выражение (int &) f
оценивается как 2
.
Правильный способ действительно "отбросить" указатель void *
- это сделать (A *) p
, а не (A *&) p
.Результатом (A *) p
действительно будет исходное значение указателя, которым можно безопасно манипулировать с помощью арифметики указателя.Единственный правильный способ получить исходное значение как lvalue - это использовать дополнительную переменную
A *pa = (A *) p;
...
pa++;
...
. И нет никакого законного способа создать lvalue «на месте», как вы пытались сделать с помощью * 1048.* бросать.Поведение вашего кода является иллюстрацией этого.