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

Почему у нас есть reinterpret_cast на С++, когда две цепные static_cast могут выполнять свою работу?

Скажем, что я хочу отбрасывать A* в char* и наоборот, у нас есть два варианта (я имею в виду, что многие из нас думают, что у нас есть два выбора, потому что оба, похоже, работают! Следовательно, путаница!):

struct A
{
    int age;
    char name[128];
};

A a;
char *buffer = static_cast<char*>(static_cast<void*>(&a)); //choice 1
char *buffer = reinterpret_cast<char*>(&a); //choice 2

Оба работают нормально.

//convert back
A *pA = static_cast<A*>(static_cast<void*>(buffer)); //choice 1
A *pA = reinterpret_cast<A*>(buffer); //choice 2

Даже это прекрасно работает!

Итак, почему мы имеем reinterpret_cast в С++, когда две цепочки static_cast могут выполнять свою работу?

Некоторые из вас могут подумать, что эта тема дублирует предыдущие темы, такие как перечисленные внизу этого сообщения, но это не так. Эти темы обсуждаются только теоретически, , но ни один из них не дает даже одного примера, демонстрирующего, почему reintepret_cast действительно нужен, а два static_cast будет уверенно сбой. Я согласен, один static_cast потерпит неудачу. Но как насчет двух?

Если синтаксис двух прикованных static_cast выглядит громоздким, тогда мы можем написать шаблон функции, чтобы сделать его более удобным для программистов:

template<class To, class From>
To any_cast(From v)
{
    return static_cast<To>(static_cast<void*>(v));
}

И тогда мы можем использовать это, как:

char *buffer = any_cast<char*>(&a); //choice 1
char *buffer = reinterpret_cast<char*>(&a); //choice 2

//convert back
A *pA = any_cast<A*>(buffer); //choice 1
A *pA = reinterpret_cast<A*>(buffer); //choice 2

Также см. эту ситуацию, когда any_cast может быть полезна: Правильное кастинг для функций чтения и записи fstream.

Итак, мой вопрос в основном,

  • Почему у нас есть reinterpret_cast в С++?
  • Пожалуйста, покажите мне хотя бы один пример, где два прикованных static_cast наверняка не смогут выполнить одну и ту же работу?

4b9b3361

Ответ 1

Есть вещи, которые reinterpret_cast может сделать, что никакая последовательность static_cast не может выполнять (все из С++ 03 5.2.10):

  • Указатель может быть явно преобразован в любой целочисленный тип, достаточно большой для его хранения.

  • Значение типа интегрального типа или перечисления может быть явно преобразовано в указатель.

  • Указатель на функцию может быть явно преобразован в указатель на функцию другого типа.

  • rvalue типа "указатель на элемент X типа T1" может быть явно преобразован в rvalue типа "указатель на элемент Y типа T2", если T1 и T2 - оба типа функций или оба типа объектов.

Кроме того, из С++ 03 9.2/17:

  • Указатель на объект POD-структуры, подходящим образом преобразованный с помощью reinterpret_cast, указывает на его начальный член (или если этот элемент является битовым полем, а затем блоком, в котором он находится) и наоборот.

Ответ 2

Вам понадобится reinterpret_cast, чтобы получить указатель с жестко заданным адресом (например здесь):

int* pointer = reinterpret_cast<int*>( 0x1234 );

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

Ответ 3

Конкретный пример:

char a[4] = "Hi\n";
char* p = &a;

f(reinterpret_cast<char (&)[4]>(p));  // call f after restoring full type
      // ^-- any_cast<> can't do this...

// e.g. given...
template <typename T, int N>   // <=--- can match this function
void f(T (&)[N]) { std::cout << "array size " << N << '\n'; }

Ответ 4

Кроме практических причин, которые другие давали там, где есть разница в том, что они могут сделать, это хорошо, потому что он выполняет другую работу.

static_cast говорит, пожалуйста, преобразуйте данные типа X в Y. reinterpret_cast говорит, что интерпретируйте данные в X как Y.

Вполне возможно, что базовые операции одинаковы, и что либо будет работать во многих случаях. Но есть концептуальная разница между словами, пожалуйста, конвертируйте X в Y и говорите "да, я знаю, что эти данные объявлены как X, но, пожалуйста, используйте его, как если бы это был действительно Y".

Ответ 5

Насколько я могу судить о вашем выборе 1 (двухкопанный static_cast) - это страшное поведение undefined. Static cast только гарантирует, что указатель на void *, а затем обратно на оригинальный указатель работает таким образом, что полученный указатель от них до конверсий по-прежнему указывает на исходный объект. Все другие преобразования - UB. Для указателей на объекты (экземпляры определяемых пользователем классов) static_cast может изменять значение указателя.

Для reinterpret_cast - только изменяет тип указателя и насколько я знаю - он никогда не затрагивает значение указателя.

Таким образом, технически говоря, два варианта не эквивалентны.

EDIT:. Для справки static_cast описывается в разделе 5.2.9 текущего проекта С++ 0x (извините, не имеет стандарта С++ 03, проект, который я считаю текущим, n3225.pdf). Он описывает все разрешенные преобразования, и я думаю, что ничего конкретно не указано = UB. Таким образом, он может взорвать ваш компьютер, если он захочет сделать это.

Ответ 6

Посмотрите, люди, вам действительно не нужны reinterpret_cast, static_cast или даже два других стили С++ (dynamic * и const).

Использование стилей стиля C является более коротким и позволяет вам делать все, что вам нужно сделать с помощью четырех стилей в стиле С++.

anyType someVar = (anyOtherType)otherVar;

Итак, зачем использовать стили в стиле С++? Читаемость. Во-вторых: потому что более ограничительные литья позволяют повысить безопасность кода.

* хорошо, вам может потребоваться динамическое

Ответ 7

Использование стилей C Style не безопаснее. Он никогда не проверяет, могут ли быть смешаны разные типы. С++ casts поможет вам убедиться, что тики выполняются в соответствии с связанными объектами (на основе используемого вами роли). Это более рекомендуемый способ использования отливок, чем использование традиционных стилей C Style, которые всегда вредны.