Wiki
C++ Type Conversion
Existing English translations were reused. 2 current source blocks remain in Simplified Chinese, without triggering paid translation.
1.What problem does this section solve?
C++ It is common to encounter scenarios where "one type is used as another type", for example:
- Convert
doubletoint. - Convert
enum classto an integer. - Safely convert a base class pointer to a derived class pointer.
- Temporarily remove the
constqualifier to accommodate legacy interfaces. - In low-level code, convert among pointers, integers, and byte views.
C-style cast syntax (目标类型)表达式 can do many things, but that is exactly the problem: it is too "all-purpose," and readers of the code can hardly tell at a glance whether this conversion is an ordinary numeric conversion, an inheritance hierarchy conversion, removing const , or a dangerous low-level reinterpretation.
C++ provides four more explicit type conversion operators, letting the conversion intent be written in the code.
2.implicit conversion and explicit conversion
Type conversion can be divided into implicit conversion and explicit conversion.
2.1.Implicit conversion
An implicit conversion is a conversion performed automatically by the compiler; the conversion operation does not need to be written in the code. For example:
int count = 10;
double result = count;
Here count is int,而 result is double。The compiler automatically converts the integer 10 to the floating-point number 10.0. This is implicit conversion.
Implicit conversions are convenient to write, but some conversions may cause data loss:
double price = 19.8;
int whole_price = price;
This code usually compiles, but whole_price yields 19 and the fractional part is discarded. Because the conversion is not explicitly written, it is also easy to overlook when reading the code.
2.2.explicit conversion
An explicit conversion is a conversion that the programmer explicitly requests in the code. For example:
double price = 19.8;
int whole_price = static_cast<int>(price);
Here, the static_cast<int> explicitly indicates: the programmer knows that price will be converted to int and also accepts that the fractional part will be discarded.
The difference between the two can be simply understood as:
- Implicit conversion: the compiler automatically converts based on context.
- Explicit conversion: the programmer explicitly writes in the code what type to convert to.
Implicit conversions are not necessarily unsafe, and explicit conversions do not necessarily mean safety. The main value of writing out a conversion explicitly is to make the conversion intent clearer and to have the compiler perform checking according to the specified conversion method.
The following described static_cast、dynamic_cast、const_cast and reinterpret_cast are all explicit type conversions provided by C++.
3.What is this feature?
C++ The four explicit type conversions are:
| Conversion method | Primary use | Safety level |
|---|---|---|
static_cast<T>(expr) |
Conventional type conversion, such as numeric, enum, and parent-child class upcasting | Commonly used, compile-time checking |
dynamic_cast<T>(expr) |
Safe downcasting of polymorphic types | Runtime checking, returns nullptr on failure or throws an exception |
const_cast<T>(expr) |
Add or remove const / volatile qualifiers |
Use very sparingly |
reinterpret_cast<T>(expr) |
Low-level binary reinterpretation, such as converting between pointers and integers | Most dangerous, use sparingly |
Principle: Avoid explicit casts whenever possible; when a cast is necessary, prefer the conversion with the narrowest semantics that best conveys the intent.
4.C++ Standard Edition
C++98 already provides these four conversion forms; in actual engineering, they are still recommended to replace C-style casts.
5.Required header files
Type conversion itself does not require additional header files. The libraries used in the example code require corresponding header files, for example:
#include <cstdint>
#include <iostream>
Run result: This code block does not contain the main function; it is a syntax, interface, or multi-file example and cannot be run independently, so there is no terminal output.
6.Basic syntax
目标类型 value = static_cast<目标类型>(表达式);
目标类型 value = dynamic_cast<目标类型>(表达式);
目标类型 value = const_cast<目标类型>(表达式);
目标类型 value = reinterpret_cast<目标类型>(表达式);
Run result: This code block does not contain the main function; it is a syntax, interface, or multi-file example and cannot be run independently, so there is no terminal output.
7.Example code
7.1.Example 1: static_cast Handle regular conversion
static_cast Suitable for explicit, conventional conversions that can be checked at compile time. For example, numeric conversions,enum class to integer, a base class pointer pointing to a derived class object, etc.
#include <iostream>
enum class Status
{
ok = 200,
not_found = 404
};
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
double score = 89.7;
int integer_score = static_cast<int>(score);
Status status = Status::not_found;
int status_code = static_cast<int>(status);
std::cout << "integer_score = " << integer_score << "\n";
std::cout << "status_code = " << status_code << "\n";
// 返回 0 表示程序正常结束。
return 0;
}
Running result:
integer_score = 89
status_code = 404
7.2.Example 2: dynamic_cast Safely determine the actual object type
dynamic_cast Commonly used for polymorphic base classes. When downcasting, it checks the object's true type at runtime.
#include <iostream>
struct Animal
{
virtual ~Animal() = default;
};
struct Cat : Animal
{
void meow() const
{
std::cout << "cat: meow\n";
}
};
struct Dog : Animal
{
};
void try_meow(Animal* animal)
{
Cat* cat = dynamic_cast<Cat*>(animal);
if (cat != nullptr)
{
cat->meow();
}
else
{
std::cout << "not a cat\n";
}
}
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
Cat cat;
Dog dog;
try_meow(&cat);
try_meow(&dog);
// 返回 0 表示程序正常结束。
return 0;
}
Running result:
cat: meow
not a cat
Note:
dynamic_castWhen used on a pointer, a failed cast yieldsnullptr; when used on a reference, a failed cast throwsstd::bad_cast. It requires the base class to have at least one virtual function, usually a virtual destructor.
7.3.Example 3:const_cast Only change the const qualifier
const_cast can only be added or removed, const / volatile qualification, cannot convert int to double, nor can two unrelated types be converted to each other.
#include <iostream>
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
int value = 10;
const int& readonly_ref = value;
int& writable_ref = const_cast<int&>(readonly_ref);
writable_ref = 20;
std::cout << "value = " << value << "\n";
// 返回 0 表示程序正常结束。
return 0;
}
Running result:
value = 20
In this example,value itself is an ordinary int variable, not const int.
int value = 10;
const int& readonly_ref = value;
这两行代码的意思是:readonly_ref 是 value 的一个“只读引用”。它并没有创建一个新的变量,而是给 value 起了一个只能读取、不能修改的别名。
In other words:
readonly_ref
Essentially, it still points to the original value.
Next:
int& writable_ref = const_cast<int&>(readonly_ref);
This line of code uses const_cast to remove the readonly_ref qualifier on const making it a modifiable reference again. Since it still refers to the original value so:
writable_ref = 20;
Essentially, it is modifying:
value = 20;
Therefore, the final output is:
value = 20
It should be noted that this example can be modified successfully because the original object value itself is not const; we are only temporarily referencing it in the read-only way of const int&.
If the original object itself is const, forcing modification with const_cast is undefined behavior, for example:
const int value = 10;
const int& readonly_ref = value;
int& writable_ref = const_cast<int&>(readonly_ref);
writable_ref = 20; // 未定义行为
So it can be simply remembered:
const_castIt just changes the "read-only view" back to a "writable view"; it does not copy into a new variable, nor does it truly and safely turn an inherently constant value into an ordinary variable.
7.4.Example 4:reinterpret_cast Low-level reinterpretation
reinterpret_cast means "interpret this binary data as another type." It bypasses many type system protections and should usually only appear in very low-level code.
For example, converting a pointer to an integer-form address, and then converting this integer address back to a pointer.
reinterpret_castIt can be used to perform some relatively "low-level" type conversions, such as converting a pointer to an integer-form address, and then converting that integer address back to a pointer.
It should be noted that what is converted here is theaddressnot the value stored in the variable.
#include <cstdint>
#include <iomanip>
#include <iostream>
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
int value = 42;
int* p = &value;
std::uintptr_t raw = reinterpret_cast<std::uintptr_t>(p);
int* again = reinterpret_cast<int*>(raw);
std::cout << "value = " << value << "\n";
std::cout << "p = " << p << "\n";
std::cout << "raw = " << raw << "\n";
std::cout << "raw(hex) = 0x" << std::hex << raw << std::dec << "\n";
std::cout << "again = " << again << "\n";
std::cout << "*again = " << *again << "\n";
std::cout << std::boolalpha;
std::cout << "same pointer = " << (p == again) << "\n";
// 返回 0 表示程序正常结束。
return 0;
}
Example run result:
value = 42
p = 0x7ffd60a576d4
raw = 140726224910036
raw(hex) = 0x7ffd60a576d4
again = 0x7ffd60a576d4
*again = 42
same pointer = true
Among them, the specific address value may differ each time it runs, so your run result may not be exactly the same as the above.
In this code:
int value = 42;
int* p = &value;
value is an ordinary int variable, and the value stored in it is 42.
p is a int* pointer that stores the address of value rather than the value of value.
That is to say,p what is stored inside is not:
42
Instead, it is an address like this:
0x7ffd60a576d4
Next:
std::uintptr_t raw = reinterpret_cast<std::uintptr_t>(p);
This line of code converts the pointer p to an integer.
std::uintptr_t is an unsigned integer type, and its purpose is to hold a pointer address as safely as possible.
So if:
p = 0x7ffd60a576d4
Then raw stores the integer form corresponding to this address.
For example:
raw = 140726224910036
This is just the decimal representation of the same address.
The following line of code:
std::cout << "raw(hex) = 0x" << std::hex << raw << std::dec << "\n";
is to print raw in hexadecimal. This way, you can see that the hexadecimal form of raw and the address printed by pointer p are basically the same.
Then:
int* again = reinterpret_cast<int*>(raw);
This line of code converts the address in integer form raw back to a int* pointer.
In other words:
p 指向 value
again 也指向 value
Therefore:
std::cout << "*again = " << *again << "\n";
Here, *again is accessing the variable pointed to by again.
Because again is redirected back to the original value,so:
*again
Equivalent to:
value
Therefore, the output result is:
*again = 42
Finally:
std::cout << "same pointer = " << (p == again) << "\n";
This line of code compares whether the addresses stored in the two pointers p and again are the same.
Since they all point to value the result is:
same pointer = true
The entire process can be understood as:
value 是一个变量,值是 42
p 保存 value 的地址
raw 把这个地址用整数形式保存下来
again 又把这个整数地址转换回指针
所以 again 重新指向 value
So the key point of this code is not to convert 42 to an integer, but to convert the value'saddressto an integer.
Simple memory:
int* p = &value;
Indicates:
拿到 value 的地址
std::uintptr_t raw = reinterpret_cast<std::uintptr_t>(p);
Indicates:
把地址当成整数保存
int* again = reinterpret_cast<int*>(raw);
Indicates:
把整数地址再变回指针
*again
Indicates:
通过这个地址找到原来的 value
One-sentence summary:
reinterpret_cast 在这个例子里转换的是“地址的表示形式”,不是变量里面的值。value 的值是 42,而 raw 保存的是 value 的地址。
8.How to Choose Among the Four Conversions
| Requirement | Recommended写法 | Description |
|---|---|---|
double cast to int |
static_cast<int>(x) |
Explicitly acknowledges possible loss of fractional part |
enum class cast to integer |
static_cast<int>(e) |
Enum classes are not implicitly converted to integers |
| Base class pointer to derived class pointer, and the actual type is uncertain | dynamic_cast<Derived*>(p) |
Conversion failure can be checked with nullptr |
remove const adapt to legacy interface |
const_cast<T*>(p) |
Provided that the legacy interface does not modify the actual const object |
| Pointer-to-integer conversion and low-level byte interpretation | reinterpret_cast<T>(x) |
Very low-level; avoid if possible |
9.Type Conversion Comparison: Does C-style casting increase runtime overhead?
static_cast<T>(expr)Although angle brackets are used, making it look somewhat like a template, it is not a template but rather a C++ built-in type conversion operator in the language. Here, T indicates the target type to be converted to.
Compared with a functionally equivalent C-style cast, C++ explicit type conversion usually does not incur additional runtime overhead:
| Conversion method | Typical runtime overhead |
|---|---|
static_cast |
No additional overhead is introduced by using this form |
dynamic_cast |
The object's actual type needs to be checked at runtime, which may incur time and program size overhead |
const_cast |
Usually only changes the way the compiler performs type checking, with no runtime operation |
reinterpret_cast |
Usually only changes the way the compiler interprets the data, with no additional conversion step |
For example, the following two expressions represent the same numerical conversion:
double value = 3.14;
int a = (int)value;
int b = static_cast<int>(value);
Compilers typically generate identical or equivalent machine instructions for them.static_castThe advantage of is not running faster, but that the conversion intent is clearer and stricter compile-time checks can be obtained.
However, it is necessary to distinguish between "the overhead of conversion syntax" and "the overhead of the conversion operation itself." For example, converting a floating-point number to an integer may require executing actual processor instructions:
double value = 3.14;
int result = static_cast<int>(value);
The runtime overhead that may arise here comes from the operation of converting double to int", not from static_cast syntax. Even if changed to an implicit conversion or C-style cast, this conversion operation still exists.
Therefore, simply remember: choosing an explicit type conversion of C++ is mainly to make type checking stricter and code intent clearer; except for cases requiring runtime checks of dynamic_cast, it is usually not slower than a C-style cast just because of a different syntax.
10.Why C-style casts are not recommended
C-style casts are short to write:
int n = (int)3.14;
Run result: This code block does not contain the main function; it is a syntax, interface, or multi-file example and cannot be run independently, so there is no terminal output.
It is more recommended to write it as:
int n = static_cast<int>(3.14);
Run result: This code block does not contain the main function; it is a syntax, interface, or multi-file example and cannot be run independently, so there is no terminal output.
A C-style cast may combine multiple conversion capabilities behind the scenes, potentially acting like static_cast, or like const_cast,even approaching reinterpret_cast. The lower-level the code and the more complex the types, the greater the risk of this “not making it clear.”
11.Common Errors
11.1.Use static_cast to do an unsafe downcast
Animal* animal = new Dog;
Cat* cat = static_cast<Cat*>(animal); // 危险:编译可能通过,但真实对象不是 Cat
Run result: This code block does not contain the main function; it is a syntax, interface, or multi-file example and cannot be run independently, so there is no terminal output.
If the actual type is uncertain, use dynamic_cast.
11.2.Modify the actual const object
const int value = 10;
int& ref = const_cast<int&>(value);
ref = 20; // 未定义行为
Run result: This code block does not contain the main function; it is a syntax, interface, or multi-file example and cannot be run independently, so there is no terminal output.
11.3.Misuse reinterpret_cast
double d = 3.14;
int* p = reinterpret_cast<int*>(&d); // 危险:把 double 对象当 int 对象访问
Run result: This code block does not contain the main function; it is a syntax, interface, or multi-file example and cannot be run independently, so there is no terminal output.
12.Usage Recommendations
- For ordinary conversions, prefer
static_cast. - For polymorphic downcasts, prefer
dynamic_castand check for failure. const_castUse only to adapt to legacy interfaces; do not use it to modify the realconstobject.reinterpret_castPlace only in very low-level, clearly bounded code, and encapsulate it centrally.- Do not use C-style casts just to save a few keystrokes.
13.Summary
static_cast: Regular conversion, the most commonly used.dynamic_cast: Polymorphic type-safe downcast.const_cast: Only changesconst/volatilequalifiers.reinterpret_cast: Low-level reinterpretation, highest risk.
One-sentence takeaway: Avoid it if possible; when it must be used, state the conversion intent clearly.