Наткнулся на полезное видео про приведение типов в C++. Освежил знания в голове. Чтобы не забыть, тут запишу, вдруг пригодится. Видео встроено в конце статьи, можете промотать до него, если текст вам неинтересен, видео более подробное.
Автору видео и ведущему канала моё почтение — ролик про lvalue/rvalue я так полностью и не усвоил. Итак, основные способы приведения типов в языке С++ следующие:
static_cast
Проверка производится во время компиляции, сообщение о невозможности операции будет получено в момент сборки приложения. Осуществляет явное допустимое приведение типов данных, в основном, используется для преобразования между числовыми типами данных.
Нужно быть внимательным, если используете для приведения указателя на родительский класс (Base *
) к указателю на наследника (Derived *
), он не выполняет проверку на корректность приведения (только порядок наследования) в этом случае и молча создаст проблемы — не надо так! Лучше использовать для этого dynamic_cast
.
Личный опыт: использую для того, чтобы компилятор не ругался на signed/unsigned типы и float/double.
dynamic_cast
Проверка производится во время исполнения. В случае неправильного приведения указателей будет возвращен 0, в случае ссылок исключение std::bad_cast
. Использует RTTI, работает динамически, может применяться только к классам, которые имеют хоть одну виртуальную функцию (в которых есть таблица виртуальных методов).
Личный опыт: dynamic_cast
предпочитаю использовать только для приведения указателя на родительский класс (Base *
) к указателю на наследника (Derived *
) с проверкой на 0. Никогда не использую его для ссылок, чтобы не париться с исключениями.
reinterpret_cast
Приведение указателей любых типов друг в друга без проверки, на ваш страх и риск. Говорит считать кусок памяти одного типа куском памяти другого типа. Был указатель на массив uint8_t, стал указатель на массив float. Что интересно: не генерирует код, не создаёт новых переменных. Он не изменяет битовое представление данных, а просто интерпретирует их по-другому.
Личный опыт: reinterpret_cast
обычно использую, чтобы передавать различные типы через сырой указатель на область памяти (void*
), но однажды я посмотрел, как побитово устроен float
, и под впечатлением от увиденного написал целую статью.
const_cast
Убирает const
и volatile
модификаторы с переменной.
Личный опыт: даже не знаю, для чего он вам может понадобиться. В голову приходит перегрузка операторов:
T &operator[](index)
и const T& operator[](index) const
и вызов константного оператора из неконстантного с помощью const_cast
, чтобы не дублировать код.
C-style cast
Применяет цепочку преобразований, сначала const_cast
, потом static_cast
, потом static_cast
от const_cast
, потом reinterpret_cast
, потом reinterpret_cast
от const_cast
. Здорово, правда? Не рекомендую вам его использовать в C++.
Личный опыт: грешен, использую, когда лень много писать, но стараюсь его избегать и вам советую, потому что его невозможно найти поиском по файлу, только просматривая каждую строку глазами, а они не вечные.
Вообще, всю жизнь считал, что это аналог reinterpret_cast
, но я ошибался.
std::static_pointer_cast
Аналог static_cast
для приведения умных указателей std::shared_ptr
.
Личный опыт: std::static_pointer_cast
использую для приведения умных указателей на базовый класс (std::shared_ptr<Base>
) к умному указателю на наследованный класс (std::shared_ptr<Derived>
).
std::dynamic_pointer_cast
Аналог dynamic_cast
для умных указателей std::shared_ptr
.
Личный опыт: никогда не видел вживую и не использовал, узнал про него в сейчас лет.
std::const_pointer_cast
Аналог const_cast
для умных указателей std::shared_ptr
.
Личный опыт: узнал про последние два только во время написания этой статьи, никогда не использовал. Буду «блистать» на собесах.