const_cast, reinterpret_cast, using and typedef
static_cast changes the type of a value, but it cannot remove const from a pointer:
int main() {
const int limit = 10;
int* p = static_cast<int*>(&limit); // ✗
return *p;
}error: static_cast from 'const int *' to 'int *' is not allowedOther named casts remove const and reinterpret pointers, and each of them does one job.
const_cast
const_cast removes const from a pointer or a reference and changes nothing else: const_cast<int*>(&limit) is an int* to the same limit. It does not change the type of a value, so const_cast<int>(2.5) does not compile. It is used when a function takes an int* but only reads the value:
#include <iostream>
void print_value(int* p) {
std::cout << *p << '\n';
}
int main() {
const int limit = 10;
print_value(const_cast<int*>(&limit)); // 10
}Without const_cast, such a function cannot get a const variable's address: the compiler does not turn const int* into int* itself.
reinterpret_cast and void*
With pointers, reinterpret_cast gives a pointer to another type at the same address. The bytes in memory stay the same; only the type they are read as changes. Reading a float through an int* is undefined behavior, so a beginner almost never needs this cast. It does not convert numbers: reinterpret_cast<int>(2.5) does not compile.
Some pointers change type without any cast. An int* converts implicitly to void*, a pointer to a value of unknown type, and static_cast<int*> turns it back. The fourth named cast, dynamic_cast, works only with pointers and references to classes.
Type aliases
A type alias is a second name for an existing type. It is introduced with using: using Score = long long;. The older notation for the same thing is typedef long long Score;. There the new name comes last, while using puts it on the left, as in an assignment.
#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
}An alias does not create a new type: to the compiler, Score is long long, and ScoreList is std::vector<long long>. If Celsius and Fahrenheit are both aliases of double, a function with a Celsius parameter silently accepts a Fahrenheit. An alias makes code clearer, and the type for the whole program changes in one line. Unlike #define, an alias obeys scope: declared inside a function, it is visible only there.
Common mistake
const_cast is used to write to a variable declared const:
int main() {
const int limit = 10;
int* p = const_cast<int*>(&limit);
*p = 20; // ✗
return limit;
}The compiler builds the program with no errors or warnings. But limit is declared const, and writing to it through p is undefined behavior: the standard does not say what main returns. Write through const_cast only to a variable declared without const. If the value has to change, remove const from the declaration.