C++类型转化表达式总结

const_cast

根据 http://www.cplusplus.com 的说法,const_cast用于对象指针或引用的const的添加和删除操作

例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// const_cast
#include <iostream>
using namespace std;

void print (char * str)
{
cout << str << '\n';
}

int main () {
const char * c = "sample text";
print ( const_cast<char *> (c) );
return 0;
}

输出为

1
sample text

http://en.cppreference.com 上有更多的信息

函数指针和成员函数指针不可用于const_cast

const_cast可以使实际指代const对象的到非const类型的引用或指针,但是通过非const访问路径修改const对象则是未定义行为。

更详细的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>

struct type {
type() :i(3) {}
void m1(int v) const {
// this->i = v; // 编译错误:这是指向 const 的指针
const_cast<type*>(this)->i = v; // 只要对象不是 const 就 OK
}
int i;
};

int main()
{
int i = 3; // i 不声明为 const
const int& cref_i = i;
const_cast<int&>(cref_i) = 4; // OK :修改 i
std::cout << "i = " << i << '\n';

type t; // note, if this is const type t;, then t.m1(4); is UB
t.m1(4);
std::cout << "type::i = " << t.i << '\n';

const int j = 3; // j 声明为 const
int* pj = const_cast<int*>(&j);
// *pj = 4; // 未定义行为!

void (type::*mfp)(int) const = &type::m1; // 指向成员函数指针
// const_cast<void(type::*)(int)>(mfp); // 编译错误: const_cast 对函数指针不起作用
}

输出

1
2
i = 4
type::i = 4

reinterpret_cast

reinterpret_cast可以在任意类型指针进行转换,哪怕是不相关的类型。

还可用于指针或引用与整型的相互转化,不单是int,还包括short、long等整数类型。

原因在于reinterpret_cast是“simple binary copy”,cplusplus的原话是“The operation result is a simple binary copy of the value from one pointer to the other. All pointer conversions are allowed: neither the content pointed nor the pointer type itself is checked.”

经过测试,整型与char *之间的转化也可以正常进行

cppreference的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <cstdint>
#include <cassert>
#include <iostream>
int f() { return 42; }
int main()
{
int i = 7;

// 指针到整数并转回
uintptr_t v1 = reinterpret_cast<uintptr_t>(&i); // static_cast 为错误
std::cout << "The value of &i is 0x" << std::hex << v1 << '\n';
int* p1 = reinterpret_cast<int*>(v1);
assert(p1 == &i);

// 到另一函数指针并转回
void(*fp1)() = reinterpret_cast<void(*)()>(f);
// fp1(); 未定义行为
int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
std::cout << std::dec << fp2() << '\n'; // 安全

// 通过指针的类型别名使用
char* p2 = reinterpret_cast<char*>(&i);
if(p2[0] == '\x7')
std::cout << "This system is little-endian\n";
else
std::cout << "This system is big-endian\n";

// 通过引用的类型别名使用
reinterpret_cast<unsigned int&>(i) = 42;
std::cout << i << '\n';

const int &const_iref = i;
// int &iref = reinterpret_cast<int&>(const_iref); // 编译错误——不能去除 const
// 必须用 const_cast 代替: int &iref = const_cast<int&>(const_iref);
}

输出

1
2
3
4
The value of &i is 0x7fff352c3580
42
This system is little-endian
42

static_cast

static_cast可以在有联系的类型之间进行指针转换(between pointers to related classes),不但包括派生类转换基类,也包括基类转换派生类。

static_cast还可以完成以下两种

Convert from void to any pointer type.可以有void 到任何类型

Convert integers, floating-point values and enum types to enum types.由整数、浮点、枚举值到枚举值

此外

Explicitly call a single-argument constructor or a conversion operator. 调用单一参数的构造函数或转换的重载符号

Convert to rvalue references. (由左值)转化成右值的引用

Convert enum class values into integers or floating-point values. 枚举类转化为整数、浮点数

Convert any type to void, evaluating and discarding the value. 任意类型到void,求值后舍弃该值

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <vector>
#include <iostream>

struct B {
int m = 0;
void hello() const {
std::cout << "Hello world, this is B!\n";
}
};
struct D : B {
void hello() const {
std::cout << "Hello world, this is D!\n";
}
};

enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };

int main()
{
// 1: 初始化转换
int n = static_cast<int>(3.14);
std::cout << "n = " << n << '\n';
std::vector<int> v = static_cast<std::vector<int>>(10);
std::cout << "v.size() = " << v.size() << '\n';

// 2: 静态向下转型
D d;
B& br = d; // 通过隐式转换向上转型
br.hello();
D& another_d = static_cast<D&>(br); // 向下转型
another_d.hello();

// 3: 左值到亡值
std::vector<int> v2 = static_cast<std::vector<int>&&>(v);
std::cout << "after move, v.size() = " << v.size() << '\n';

// 4: 弃值表达式
static_cast<void>(v2.size());

// 5. 隐式转换的逆
void* nv = &n;
int* ni = static_cast<int*>(nv);
std::cout << "*ni = " << *ni << '\n';

// 6. 数组到指针后随向上转型
D a[10];
B* dp = static_cast<B*>(a);

// 7. 有作用域枚举到 int 或 float
E e = E::ONE;
int one = static_cast<int>(e);
std::cout << one << '\n';

// 8. int 到枚举,枚举到另一枚举
E e2 = static_cast<E>(one);
EU eu = static_cast<EU>(e2);

// 9. 指向成员指针向上转型
int D::*pm = &D::m;
std::cout << br.*static_cast<int B::*>(pm) << '\n';

// 10. void* 到任何类型
void* voidp = &e;
std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}

输出

1
2
3
4
5
6
7
8
n = 3
v.size() = 10
Hello world, this is B!
Hello world, this is D!
after move, v.size() = 0
*ni = 3
1
0

dynamic_cast

dynamic_cast只能用于类或者void *的指针、引用转换,同时必须为多态以使用运行时检查的

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>

struct V {
virtual void f() {}; // 必须为多态以使用运行时检查的 dynamic_cast
};
struct A : virtual V {};
struct B : virtual V {
B(V* v, A* a) {
// 构造中转型(见后述 D 的构造函数中的调用)
dynamic_cast<B*>(v); // 良好定义: v 有类型 V* , B 的 V 基类,产生 B*
dynamic_cast<B*>(a); // 未定义行为: a 有类型 A* , A 非 B 的基类
}
};
struct D : A, B {
D() : B((A*)this, this) { }
};

struct Base {
virtual ~Base() {}
};

struct Derived: Base {
virtual void name() {}
};

int main()
{
D d; // 最终导出类
A& a = d; // 向上转型,可以用 dynamic_cast ,但不必须
D& new_d = dynamic_cast<D&>(a); // 向下转型
B& new_b = dynamic_cast<B&>(a); // 侧向转型


Base* b1 = new Base;
if(Derived* d = dynamic_cast<Derived*>(b1))
{
std::cout << "downcast from b1 to d successful\n";
d->name(); // 调用安全
}

Base* b2 = new Derived;
if(Derived* d = dynamic_cast<Derived*>(b2))
{
std::cout << "downcast from b2 to d successful\n";
d->name(); // 调用安全
}

delete b1;
delete b2;
}

输出

1
downcast from b2 to d successful

dynamic_cast与static_cast在类转换上的不同在于,dynamic_cast会确保转化成功,在转化不成功时会设置为NULL,如上例中

1
Derived* d = dynamic_cast<Derived*>(b1)