Перейти к содержимому
Теория · Приведения и псевдонимы · урок 3 из 6

const_cast, reinterpret_cast, using и typedef

static_cast меняет тип значения, но снять const с указателя он не может:

C++
int main() {
    const int limit = 10;
    int* p = static_cast<int*>(&limit);  // ✗
    return *p;
}
TEXT
error: static_cast from 'const int *' to 'int *' is not allowed

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

const_cast

const_cast снимает const с указателя или ссылки (reference) и больше ничего не меняет: const_cast<int*>(&limit) — это int* на тот же limit. Тип значения он не меняет, поэтому const_cast<int>(2.5) не скомпилируется. Его применяют, когда функция принимает int*, но значение только читает:

C++
#include <iostream>

void print_value(int* p) {
    std::cout << *p << '\n';
}

int main() {
    const int limit = 10;
    print_value(const_cast<int*>(&limit));  // 10
}

Без const_cast адрес const-переменной в такую функцию не передать: const int* в int* компилятор сам не превращает.

reinterpret_cast и void*

С указателями reinterpret_cast даёт указатель на другой тип с тем же адресом. Байты в памяти остаются прежними, меняется только тип, которым их читают. Прочитать float через int* — неопределённое поведение, поэтому начинающему это приведение почти не нужно. Числа reinterpret_cast не преобразует: reinterpret_cast<int>(2.5) не скомпилируется.

Некоторые указатели меняют тип и без приведения. int* неявно превращается в void* — указатель на значение неизвестного типа, а обратно его возвращает static_cast<int*>. Четвёртое именованное приведение, dynamic_cast, работает только с указателями и ссылками на классы.

Псевдонимы типов

Псевдоним типа (type alias) — второе имя существующего типа. Его вводят через using: using Score = long long;. Старая запись того же — typedef long long Score;: новое имя в ней стоит в конце, а в using — слева, как в присваивании.

C++
#include <iostream>
#include <vector>

using Score = long long;
typedef std::vector<Score> ScoreList;

int main() {
    ScoreList scores{1500000000, 1500000000};
    Score total = scores[0] + scores[1];
    std::cout << total << '\n';  // 3000000000
}

Псевдоним не создаёт новый тип: для компилятора Score — это long long, а ScoreList — std::vector<long long>. Если Celsius и Fahrenheit — псевдонимы double, функция с параметром (parameter) Celsius молча примет Fahrenheit. Псевдоним делает код понятнее, а тип во всей программе меняется в одной строке. В отличие от #define, псевдоним подчиняется области видимости (scope): объявленный внутри функции, он виден только в ней.

Частая ошибка

Через const_cast записывают в переменную, объявленную с const:

C++
int main() {
    const int limit = 10;
    int* p = const_cast<int*>(&limit);
    *p = 20;  // ✗
    return limit;
}

Компилятор собирает программу без ошибок и предупреждений. Но limit объявлена с const, и запись в неё через p — неопределённое поведение: стандарт не говорит, что вернёт main. Писать через const_cast можно только в переменную, объявленную без const. Если значение должно меняться, уберите const из объявления (declaration).