const_cast, reinterpret_cast, using и typedef
static_cast меняет тип значения, но снять const с указателя он не может:
int main() {
const int limit = 10;
int* p = static_cast<int*>(&limit); // ✗
return *p;
}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*, но значение только читает:
#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 — слева, как в присваивании.
#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:
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).