C++ 中的类型转换

C++ 对于类型的要求极为严格,猜想下面的代码能否通过编译?

1
2
3
array<int, 6> a1;
array<int, 3> a2;
a1 = a2;

我们可能需要在我们的代码中执行类型转换操作,例如把 int 类型的变量转变为 double 类型的变量,将派生类转变为父类…..

C++ 一共提供以下的各种类型的类型转换方式

1
2
3
4
5
6
7
8
9
10
// 旧式的类型转换
// C 语言格式
(T) expression // 将表达式转换成 T, 例如将函数 fun1 的返回值转变为 double 类型: (double) fun1(a)
// 函数类型格式
T (expression) // 将表达式转换成 T
// C++ 还提供新的类型转换形式
const_cast<T> (expression) //用来将 const 转换成 non-const
dynamic_cast<T> (expression) //用来实现“安全向下转型”
reinterpret_cast<T> (expression) //重新解释底层的 bits, 用来执行底层的转型,例如把一个指针内存中的对应的 bits 看待成 int 类型
static_cast<T> (expression) //用来执行强制类型转换,例如把 non-const 转变为 const ,或者是将 void* 类型转变为 int* 类型

使用类型转换时的注意事项

尽可能的避免类型转换
正如文章开头提到的一样,C++对于类型有着严格的规定,我们不可能想其他语言一样,用“等于”符号就能解决问题。当我们在代码中进行类型的转换时,我们通常需要按照C++规定的形式(上面给出的6种形式),才能完成类型的转换任务。

我们除了要按照C++的规定严格的执行之外,我们还需要注意 “另一些的规则”

  • 尽可能的应用新式的类型转换
    虽然旧式类型转换和新式的类型转换能够做的事情一样,但是很明显新式的类型转换更容易被辨认出来,编译器也更容易帮助我们进行检查。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //唯一推荐使用旧式类型转换的场景
    调用一个 explicit 构造函数将一个对象传递给另一个对象
    class Widget{
    public:
    explicit Widget(int size){
    //...
    }
    }
    Widget w(Wiget(1024));
  • 有些版本的 dynamic_cast 转换可能实现的很慢
  • 有些版本的 reinterpret_cast 不具有可移植性

尽可能的避免使用类型转换

首先,我们需要知道的一点是: 类型转换并不是简单的告诉编译器哪里需要执行类型转换,编译器需要做的事情实际上也发生了明显的变化。

1
2
3
4
5
//编译器对于 double 和 x 的处理,并不相同 
int x, y;
double d = (double) x / y;
// or
double d= static_cast<double> / y;

编译器需要为类型转换付出行动,类型转换是需要付出代价的

而在 《effective STL》一书中,作者更是对于随意的使用类型转换嗤之以鼻。当我们需要将标准容器(standard container)中的 const_iterator 转变成 iterator 时(从 const_iterator 到 iterator 没有显式转换),下面的代码并不能完成任务:

1
2
3
deque<int>::const_iterator ci; 
deque<int>::iterator c(ci); //不能通过编译
deque<int>::iterator c(const_cast<deque<int>::iterator>(ci)); //也不能通过编译

因为 iterator 和 const_iterator 是两个不一样的类。字面上的 const 并不能说明两者的类型并不相同。

1
2
typedef __deque_iterator<T, T&, T*>                      iterator;
typedef __deque_iterator<T, const T&, const T*> const_iterator;

作者给出的解决方案很简单,利用 distance 和 advance 来实现容器的 将 const_iterator 转换成 (non-const) iterator。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
deque<int> d;
deque<int>::const_iterator ci;
deque<int>::iterator c(d.begin());
advance(c, distance(c,ci)); // 不会编译,因为 _InputIterator 不能为两个不同的类型
advance(c, distance<deque<int>::const_iterator>(c, ci ,0)); // 指明 InputIterator 的类型 的类型为 InputIterator

//advance 的头文件定义
template <class InputIterator, class Distance>
inline void advance(InputIterator& i, Distance n)

//distance 的头文件定义(input_iterator_tag 为迭代器的类别,以便调用对应的版本)
template <class InputIterator, class Distance>
inline void __distance(InputIterator first, InputIterator last, Distance& n,
input_iterator_tag)
template <class InputIterator, class Distance>
inline void distance(InputIterator first, InputIterator last, Distance& n)