Зачем мне нужен reinterpret_cast для преобразования Fred ** const в void ** const?
У меня есть указатель const на указатель на Fred
, и я не понимаю, почему a static_cast
недостаточно.
typedef struct {
int n;
} Fred;
Fred *pFred;
Fred **const ppFred = &pFred;
void **const ppVoid = static_cast<void ** const>(ppFred);
Пожалуйста, может кто-нибудь объяснить, почему a reinterpret_cast
необходимо преобразовать указатель на Fred*
в указатель на void*
, но static_cast
прекрасно конвертировать указатель на Fred
в указатель на void
.
Ответы
Ответ 1
Нет необходимости, чтобы a Fred*
и a void*
имели одинаковый размер
и представление. (Я работал на машинах там, где их не было,
хотя это было до моих дней С++.) Когда вы конвертируете Fred*
в
void*
, вы получаете новый указатель с возможным разным размером и
представления, но нет информации о размере и
представление объекта, на которое указывает void*
. Вы знаете, что это
неизвестно, и единственный способ использовать этот void*
- вернуть его обратно в
Fred*
(по модулю такие вещи, как cv-qualifiers). Когда вы конвертируете Fred**
до void**
, вы переходите от указателя к конкретному типу (a
указатель на Fred*
) на указатель на другой конкретный тип (указатель
до void*
). И поскольку нет гарантии, что эти два конкретных
типы имеют одинаковый размер и представление, для преобразования требуется
reinterpret_cast
. void
- это специальный, небетонный тип, поэтому вы можете
static_cast
указатель на любой тип с указателем на void
и от него.
void*
- это еще один конкретный тип указателя, поэтому отбрасывание на и из
указатели на него следуют обычным правилам (и требует
reinterpret_cast
).
Во многом ситуация очень похожа на int
и double
, где
void*
играет роль int
(скажем), а Fred*
роль double
.
Нет проблемы static_cast
между int
и double
, но
для отливок между int*
и double*
требуется reinterpret_cast
.
Ответ 2
Все указатели объектов могут быть конвертированы в void*
, поэтому для этого применяется статическое преобразование. Однако преобразование между T*
и U*
в общем случае требует переинтерпретации, поскольку произвольные указатели не являются взаимозаменяемыми. (И замените T = Fred*
, U = void*
.)
Ответ 3
static_cast
не будет работать для преобразования Fred **
в void **
, потому что это не разумное преобразование: указатели на Fred*
и void*
не обязательно создаются одинаково (т.е. проблемы выравнивания на некоторых платформ). Вы можете быть уверены, что void*
, который может указывать на любой байт в памяти, может также указывать на объект Fred
, но это не так для указателей void**
.
Ответ 4
Отказ
Ниже представлено ручное размахивание, чтобы сделать вещи понятными, а не технически правильное описание.
Ручная магия
Одним из возможных способов введения void
является:
void
похож (не то же самое), что и универсальный суперкласс Java Object
.
void
можно рассматривать как абстрактную базу каждого класса и неклассового типа. (С помощью этой метафоры void
также будет квази-виртуальным базовым типом: преобразование в void*
никогда не является двусмысленным.)
Таким образом, вы можете увидеть неявное преобразование с T*
в void*
как преобразование с производной на базовую, а обратное static_cast
похоже на базу для производного переноса. Когда a void*
не указывает на T
, вы не должны делать static_cast<T*>
(когда Base*
не указывает на Derived
, вы не должны делать static_cast<Derived*>
).
Отказ от ответственности, снова
Серьезно void
является не абстрактным базовым классом и не может формально рассматриваться как один во многих случаях:
- Вы не можете формально описать
void
либо как виртуальную базу (или static_cast
сломается), либо не виртуальную базу (или преобразования в void*
будут неоднозначными при использовании множественного наследования).
- Нет типа
void&
. Эта метафория базового класса просто выходит за рамки указателей.
Пожалуйста, не говорите людям "void
является универсальным базовым классом С++, таким как Java Object". Не повторяйте ничего, что я написал здесь, без полного отказа от ответственности.
Только в некоторых случаях void
ведет себя как базовый класс с целью неявного преобразования указателей и отбрасываний.
Вы не можете писать программы, основанные на метафорах,, но на реальных правилах на С++.
Эта метафора может помочь. Или нет. В любом случае, никогда не пытайтесь делать логические выводы, основанные на метафоре.