#cpp #типы_данных #language_lawyer #неопределенное_поведение
В C++ существует оператор reinterpret_cast, смысл которого заключается в приведении между типами, несовместимыми друг с другом. Однако подобные преобразования нарушают strict aliasing rule, что провоцирует неопределённое поведение. Те же преобразования, которые этого правила не нарушают, укладываются в const_cast, static_cast и dynamic_cast. В чём же тогда заключается смысл существования данного оператора, если его использование нарушает стандарт?
Ответы
Ответ 1
reinterpret_cast используется не только для преобразования указателей одного типа в другой. Существует несколько разных преобразований. cppreference.com выделяет 11 вариантов преобразований: В свой собственный тип Указателя в интегральный тип Интегрального типа в указатель Типа std::nullptr_t в интегральный тип Указателя одного типа в указатель другого типа lvalue одного типа в ссылку на другой тип Указателя на функцию одного типа в указатель на функцию другого типа Указателя на функцию в void* Нулевого указателя любого типа в указатель любого другого типа rvalue указатель одного типа на функцию-член в указатель другого типа на функцию-член rvalue указатель члена-данных одного типа в указатель ну другой член-данных другого типа Type aliasing-правила затрагивают только пункты 5 и 6 и результат может быть безопасно использован (т.е. без нарушения strict-aliasing) в следующих случаях: Результирующий тип есть динамический тип исходного объекта Результирующий тип и динамический тип указывают на одинаковый тип T Результирующий тип есть знаковый или беззнаковый вариант типа исходного объекта Результирующий тип есть агрегатный тип или union, в котором содержится элемент или нестатический член данных, используемый в качестве исходного объекта. Т.е. можно получить указатель на структуру по указателю на её член. Результирующий тип есть базовый класс динамического типа исходного объекта и этот тип является standard-layout классом и не содержит нестатических членов-данных, и результирующий тип - первый базовый класс. Результирующий тип есть указатель на char, unsigned char или std::byte. Некоторые реализации ослабляют эти правила в качестве нестандартных расширений языка.Ответ 2
Существует ровно один вид преобразования между несовместимыми типами, не нарушающий strict aliasing rule — из произвольного указателя на указатель типа char*. То есть reinterpret_cast позволяет представить произвольный объект в виде последовательности байт (так как стандартом гарантируется однобайтовая длина char-а). Вот пример грамотного использования этого вида преобразования: templatevoid putIntoStream(const T* object, std::ostream& out) { out.write(reinterpret_cast (object), sizeof(T)); } Для всего остального есть memcpy(). Ответ 3
Несмотря на то, что использование reinterpret_cast в большинстве случаев приводит к неопределенному поведению, библиотеки могут использовать его в своих реализациях, тестируя их для конкретных компиляторов. Для пользователя библиотек поведение уже не будет неопределённым, т.к. оно проверено и документировано, но ему придётся учитывать список поддерживаемых компиляторов. Иногда это единственный способ разработки кросплатформенной библиотеки. Кроме того, всё еще есть случаи, когда приходится жертвовать переносимостью для достижения других целей (в частности, это актуально для микроконтроллеров).
Комментариев нет:
Отправить комментарий