Подтвердить что ты не робот

Зачем мне нужен 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.

4b9b3361

Ответ 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 ведет себя как базовый класс с целью неявного преобразования указателей и отбрасываний.

Вы не можете писать программы, основанные на метафорах,, но на реальных правилах на С++.

Эта метафора может помочь. Или нет. В любом случае, никогда не пытайтесь делать логические выводы, основанные на метафоре.