static_cast
В предыдущих статьях мы несколько раз упоминали оператор
Исходя из своих наблюдений, наиболее востребованным оператором приведения является
Оператор
Конечно, некоторые смысловые ошибки нельзя поймать, ведь с точки зрения типа, все хорошо. Например, приведение значения к
Правила приведения для фундаментальных (встроенных) типов в C++ определены заранее, а вот для пользовательских классов можно определить свои собственные преобразования с помощью оператора приведения к типу:
Эта ручка будет дергаться при явном и неявном приведении типов в живом примере 1:
Если такое предупреждение появляется, то вероятно, что что-то вы все таки упускаете в своем коде. Но иногда такие ситуации встречаются, когда полезная нагрузка от вашего действия есть, а предупреждение не к месту. Например, в следствие какой-нибудь препроцессорной директивы. Напоминаем, что в C++17 так же есть атрибут
Так же
Разберем эту тему подробнее, когда дойдем до динамического полиморфизма
В предыдущих статьях мы несколько раз упоминали оператор
static_cast
, поэтому мы решили затронуть еще и тему приведения типов. По мере развития серии, рассмотрим каждый из них, а завершим разбором C-style cast.Исходя из своих наблюдений, наиболее востребованным оператором приведения является
static_cast
, т.к. в основном большинство приходится на преобразование между совместимыми друг с другом типами:int32_t value_i32 = 42;
int64_t value_i64 = static_cast<int64_t>(value_i32);
float value_f32 = 42.314;
int16_t value_i16 = static_cast<int16_t>(value_f32);
Оператор
static_cast
так же проверяет корректность выполняемого приведения. Например, запрещает приведение указателя к значению:// error: invalid 'static_cast' from type 'int*' to type 'int'
static_cast<int>(&value);
Конечно, некоторые смысловые ошибки нельзя поймать, ведь с точки зрения типа, все хорошо. Например, приведение значения к
enum class
может привести к непредвиденным сценариям 🤭:enum class action_e : int { RUN = 0, FIGHT = 1 };Лучше бы их все таки дополнять еще debug-only
// Should I run or fight?
action_e action = static_cast<action_e>(2);
assert
или вообще условным ветвлением.Правила приведения для фундаментальных (встроенных) типов в C++ определены заранее, а вот для пользовательских классов можно определить свои собственные преобразования с помощью оператора приведения к типу:
operator Type()
:class specific_error_t
{
...
// Оператор приведение к типу `bool`
operator bool() const
{
return m_code < 0;
}
...
};
Эта ручка будет дергаться при явном и неявном приведении типов в живом примере 1:
specific_error_t internal_code = -1;Один из неочевидных способов применения этого оператора является приведение к типу
// Приведение `internal_code` к типу `bool`
bool has_internal_code = static_cast<bool>(internal_code);
void
. Казалось бы, зачем? Но это помогает подавить предупреждение компилятора о неиспользуемой переменной / не присвоенном значении:void foo()
{
int result = read_and_do_something();
#ifdef DEBUG
// Debug build check only
assert(result == 0);
#endif
static_cast<void>(result);
}
Если такое предупреждение появляется, то вероятно, что что-то вы все таки упускаете в своем коде. Но иногда такие ситуации встречаются, когда полезная нагрузка от вашего действия есть, а предупреждение не к месту. Например, в следствие какой-нибудь препроцессорной директивы. Напоминаем, что в C++17 так же есть атрибут
[[maybe_unused]]
, который решает эту проблему.Так же
static_cast
позволяет выполнить приведение к типу родительского класса (upcasting) и к типу наследников (downcasting) в рамках одной иерархии классов:Child *pointer = new Child();Важным моментом является тот факт, что
// Upcasting
Base *base_ptr = static_cast<Base*>(pointer);
// Downcasting
Child *child_ptr = static_cast<Child*>(base_ptr);
static_cast
не может обеспечить проверку корректности совершенного преобразования к наследнику (downcasting)! Если наследник выбран неправильно и вы допустили ошибку преобразования к другому типу, то вам все равно дадут скомпилироваться: живой пример 2. У компилятора действительно не хватает информации, чтобы это проверить на этапе компиляции. Разберем эту тему подробнее, когда дойдем до динамического полиморфизма