类型收窄指的是导致数据内容发生变化或者精度丢失的隐式类型转换。考虑下面这种情况:
struct Foo { Foo(int i) { std::cout << i << std::endl; } }; Foo foo(1.2);
以上代码在C++中能够正常通过编译,但是传递之后的i却不能完整地保存一个浮点型的数据。
上面的示例让我们对类型收窄有了一个大概的了解。具体来说,类型收窄包括以下几种情况:
1)从一个浮点数隐式转换为一个整型数,如int i=2.2。
2)从高精度浮点数隐式转换为低精度浮点数,如从long double隐式转换为double或float。
3)从一个整型数隐式转换为一个浮点数,并且超出了浮点数的表示范围,如float x=(unsigned long long)-1。
4)从一个整型数隐式转换为一个长度较短的整型数,并且超出了长度较短的整型数的表示范围,如char x=65536。
在C++98/03中,像上面所示类型收窄的情况,编译器并不会报错(或报一个警告,如Microsoft Visual C++)。这往往会导致一些隐藏的错误。在C++11中,可以通过列表初始化来检查及防止类型收窄。
请看代码清单1-10的示例。
代码清单1-10 列表初始化防止类型收窄示例
int a = 1.1; // OK int b = { 1.1 }; // error float fa = 1e40; // OK float fb = { 1e40 }; // error float fc = (unsigned long long)-1; // OK float fd = { (unsigned long long)-1 }; // error float fe = (unsigned long long)1; // OK float ff = { (unsigned long long)1 }; // OK const int x = 1024, y = 1; char c = x; // OK char d = { x }; // error char e = y; // OK char f = { y }; // OK
在上面的各种隐式类型转换中,只要遇到了类型收窄的情况,初始化列表就不会允许这种转换发生。
其中需要注意的是x、y被定义成了const int。如果去掉const限定符,那么最后一个变量f也会因为类型收窄而报错。
对于类型收窄的编译错误,不同的编译器表现并不相同。
在gcc 4.8中,会得到如下警告信息:
warning: narrowing conversion of '1.0e+40' from 'double' to 'float' inside { } [-Wnarrowing] float fb = { 1e40 }; ^ 在Microsoft Visual C++2013中,同样的语句则直接给出了一个错误: error C2397: conversion from 'double' to 'float' requires a narrowing conversion 另外,对于精度不同的浮点数的隐式转换,如下面这种: float ff = 1.2; // OK float fd = { 1.2 }; // OK (gcc)
fd的初始化并没有引起类型收窄,因此,在gcc 4.8下没有任何错误或警告。但在Microsoft Visual C++2013中,fd的初始化语句将会得到一个error C2397的编译错误。
C++11中新增的这种初始化方式,为程序的编写带来了很多便利。在后面的章节中也会经常使用这种新的初始化方式,读者可以在后面章节的示例代码中不断体会到新初始化方法带来的便捷。