Страницы

Поиск по вопросам

понедельник, 22 октября 2018 г.

Что происходит при приведении типов?

Если приведение в Си стиле (float)i; или float(i);, то по сути в обоих случаях вызывается конструктор и создается временный объект? Но а что в случае с указателями, если привести int* p к (float*)p, то почему нельзя написать float*(p) и что делается в случае с (float*)p? UPD: @VladD, За static_cast что-то не понятно. К примеру есть Си стиль: char* str = "123456789"; int* pi = (int*)str; а с static_cast будет : char* str = "123456789"; void* pv = static_cast(str); int* pi = static_cast(pv); так вот зачем к войду приводить, а не сразу к int? Это промежуточное приведение сделанное потому, что в Си нету ограничений в плане присвоения указателя одного типа указателю другого типа, а в C++ есть? То есть, получается, что если написать в С++ (int*)str, то он будет разложен на static_cast и static_cast? А reinterpret_cast каким образом тогда игнорирует это ограничение в C++? Или он тоже использует static_cast? char* str = "123456789"; int* pi = reinterpret_cast(str); @avp, я так понял, что в Си не будет работать это : int a; float b = float(a); потому что в Си нету классов и не будет вызван конструктор, а в конструкторе неявно приведен. Но а как тогда в Си работает такое : int a; float b = (float)a; ведь если в C++ при (float)a; выбирается static_cast или const_cast или reinterpret_cast, то что в Си происходит при (float)a?


Ответ

Вообще-то, приведение типов — огромная тема, в ней сброшены в одну кучу и преобразования между арифметическими типами (расширение/сужение различных видов целого, преобразование между целым и плавающей запятой и обратно), и переход вверх/вниз по иерархии наследования через указатели, и динамический контроль типов времени выполнения, и добавление/снятие модификаторов const/volatile, всё подряд. Все правила охватить — тема для раздела с исследованиями.
Продублирую ссылки на документацию:
const_cast static_cast reinterpret_cast dynamic_cast C-style cast

По поводу вашего вопроса о const_cast: смотрите.
Вы не можете сделать static_cast из char* в int* по правилам, перечисленным тут. Самым подходящим могли бы быть правила 1 и 5, но поскольку нет неявной конверсии между char* и int* (char и int не являются подклассами друг друга), то и прямой static_cast невозможен.
С другой стороны, static_cast из char* в void* возможен по правилу 1, поскольку есть неявная конверсия из любого указателя на данные в void*. Обратная конверсия из void* в int* возможна по правилу 10.
Возникает закономерный вопрос, а зачем нужны такие ограничения? Дело в следующем. Прямая конверсия запрещена, потому что использовать полученный указатель иначе чем для того, чтобы преобразовать его назад к char*, вы не имеете права по стандарту. Это происходит вследствие так называемого strict aliasing rule. Лучше всего почитать подробное объяснение с указанием причин здесь, вкратце: если указатель указывает на char, вы не можете обращаться к int'у по этому адресу. [Единственное исключение — преобразование в char*.]
Поэтому язык предохраняет вас от ошибки: если вы уж написали дважды static_cast через void*, вы должны понимать, что делаете что-то специальное.
Напротив, C-style cast — гораздо более безалаберная штука: компилятор пробует по очереди const_cast, static_cast, static_cast + const_cast, reinterpret_cast, reinterpret_cast + const_cast, и берёт первое, что подходит. Такое поведение более типично для языков типа Visual Basic'а: вы не контролируете, что именно происходит, вы говорите компилятору «короче, реши за меня сам».

C-style cast в C — единственный существующий вид явного приведения типов. Поскольку в нём нету семантики наследования, в нём все операции преобразования указателей являются по существу пустыми, и просто изменяют взгляд компилятора на данные по данному адресу. (Вообще, грубо говоря, C работает с байтами, а C++ — с абстрактными сущностями.) Тем не менее, strict alisaing rule работает и в C.

Заметьте, что в C++ преобразование указателя между базовым и производным типами может приводить к изменению адреса — в отличие от C. Это означает, что reinterpret_cast может дать неправильный результат, даже если static_cast даёт правильный! То есть, нельзя бездумно использовать reinterpret_cast в надежде, что он всё сделает правильно. И это также объясняет, почему C-style cast не сводится к reinterpret_cast

Комментариев нет:

Отправить комментарий