C++11 初始化方法(以{}方式初始化时进行的转换)
C++11将使用大括号的初始化称为列表初始化(list-initialization),因为这种初始化常用于给复杂的数据类型提供值列表。与传统的初始化方式相比,它对类型转换的要求更严格。具体地说,列表初始化不允许缩窄(narrowing),即变量的类型可能无法表示赋给它的值。例如,不允许将浮点型转换为整型。在不同的整型之间转换或将整型转换为浮点型可能被允许,条件是编译器知道目标变量能够正确地存储赋给它的值。例如,可将 long 变量初始化为 int 值,因为long 总是至少与int 一样长;相反方向的转换也可能被允许,只要int变量能够存储赋给它的long 常量:
const int code = 66;
int x = 66;
char cl31325; //narrowing,not allowedchar
char c2 ={66}; //allowed because char can hold 66
char c3{code}; // ditto
char c4={x}; //not allowed,xis not constant
x= 31325;
char c5 = x; //allowed by this form of initialization
数组的初始化规则
数组以前就可使用列表初始化,但C++11中的列表初始化新增了一些功能。首先,初始化数组时,可省略等号(=):
double earnings[4]{ 1.2e4,1.6e4,1.1e4,1.7e4 };// okay with C++11
其次,可不在大括号内包含任何东西,这将把所有元素都设置为零:
float balances[100]{}; //all elements set to 0
unsigned int counts[10] = ); //all elements set to 0
第三,列表初始化禁止缩窄转换:
long plifs[] = {25,92,3.0}; //not allowed
char slifs[4]{'h','i','1122011','\0'}; // not allowed
char tlifs[4]{'h','i','112','\0'}; // allowed
在上述代码中,第一条语句不能通过编译,因为将浮点数转换为整型是缩窄操作,即使浮点数的小数点后面为零。第二条语句也不能通过编译,因为1122011超出了char变量的取值范围(这里假设char变量的长度为8位)。第三条语句可通过编译,因为虽然112是一个int值,但它在char 变量的取值范围内。
C++11 字符串初始化
C++11也允许将列表初始化用于C-风格字符串和string 对象:
char first date[] = { "Le chapon Dodu" };
char second date[]{ "The Elegant plate" };
string third date[] = ("The Bread Bowl");
string fourth datef"Hank's Fine Eats";
编译器通过校验表来确定算术表达式中执行的转换。下面是C++11版本的校验表
如果有一个操作数的类型是long double,则将另一个操作数转换为long double。
否则,如果有一个操作数的类型是double,则将另一个操作数转换为double。
否则,如果有一个操作数的类型是float,则将另一个操作数转换为float。
否则,说明操作数整型,因此执行整型提升。
在这种情况下,如果两个操作数都是有符号或无符号的,且其中一个操作数的级别比另一个低,则转换为级别高的类型。
如果一个操作数为有符号的,另一个操作数为无符号的,且无符号操作的级别比有符号操作数高,则将有符号转换为无符号操作数所属的类型。
否则,如果有符号类型可表示无符号类型的所有可能取值,将无符号操作数转换为有符号操作数所属的类型。
否则,将两个操作数都转换为有符号类型的无符号版本。
std::vectorscores;
std::vector::iterator pv = scores.begin();
C++11 允许将其重写为下面这样:
std::vectorscores;
auto pv = scores.begin();
vector 类的功能比数组强大,但付出的代价是效率稍低。如果您需要的是长度固定的数组,使用数组是更佳的选择,但代价是不那么方便和安全。有鉴于此,C++11新增了模板类 array,它也位于名称空间 std中。与数组一样,array 对象的长度也是固定的,也使用栈(静态内存分配),而不是自由存储区,因此其效率与数组相同,但更方便,更安全。要创建aray对象,需要包含头文件array。array 对象的创建语法与vector 稍有不同:
#include
...
using namespace std;
array ai; //create array object of 5 ints
array ad = { 1.2,2.1,3.43,4.3 };
推而广之,下面的声明创建一个名为arr 的 array 对象,它包含n_elem 个类型为 typename 的元素:
array
与创建 vector 对象不同的是,n_elem 不能是变量。
在 C++11中,可将列表初始化用于 vector 和 array 对象,但在 C++98 中,不能对 vector 对象这样做。
C++11新增了一种循环:基于范围(range-based)的for 循环。这简化了一种常见的循环任务:对数组(或容器类,如 vector 和 array)的每个元素执行相同的操作,如下例所示:
double prices[5] = { 4.99,10.99,6.87,7.99,8.49 };
for (double x : prices)
cout << x << std::endl;
其中,x最初表示数组 prices 的第一个元素。显示第一个元素后,不断执行循环,而x依次表示数组的其他元素。因此,上述代码显示全部5个元素,每个元素占据一行。总之,该循环显示数组中的每个值。
要修改数组的元素,需要使用不同的循环变量语法:
for (double &x : prices)
x = x * 0.80; //20% off sale
符号&表明x是一个引用变量,让接下来的代码能够修改数组的内容,而第一种语法不能。
还可结合使用基于范围的for循环和初始化列表,这种循环主要用于各种模板容器类:
for (int x : { 3, 5, 2, 8, 6 })
cout << x << " ";
cout << '\n';
右值引用可指向右值,是使用&&声明的:
double && rref = std::sqrt(36.00);// not allowed for double &
double j=15.0;
double && jref = 2.0*j + 18.5; //not allowed for double &
std::cout << rref << '\n'; // display 6.0
std::cout << jref << '\n'; // display 48.5;
新增右值引用的主要目的是,让库设计人员能够提供有些操作的更有效实现。第18章将讨论如何使用右值引用来实现移动语义(movesemantics)。以前的引用(使用&声明的引用)现在称为左值引用。
作用:解决函数模板中,类型的声明问题
为确定类型,遍历核对表过程:
假设有如下声明:
decltype(expression) var;
1.如果 expression 是一个没有用括号括起的标识符,则 var 的类型与该标识符的类型相同,包括 const 等限定符:
double x = 5.5;
double y = 7.9;
double &rx = x;
const double *pd;
decltype(x)w; // w is type double
decltype(rx)u = y; //u is type double &
decltype(pd)v; //vis type const double *
2.如果 expression 是一个函数调用,则 var 的类型与函数的返回类型相同:
long indeed(int);
decltype(indeed(3))m;//mis type int
注意:并不会实际调用函数。编译器通过查看函数的原型来获悉返回类型,而无需实际调用函数
3.expression 是用括号括起的标识符,则var 为指向其类型的引用。:
double xx = 4.4;
decltype((xx))r2 = xx;//r2 is double &
4.如果前面的条件都不满足,则var的类型与expression 的类型相同:
int j = 3;
int &k = j;
int & n = j;
decltype(j + 6)il; // il type int
decltype(100L)i2; //i2 type long
decltype(k+n)i3; // i3 type int;
请注意,虽然k和n都是引用,但表达式k+n不是引用;它是两个imnt的和,因此类型为int。
如果需要多次声明,可结合使用typedef和decltype:
template
void ft(Tl x, T2 y)
{
typedef decltype(x + y)xytype;
xytype xpy = x + y;
xytype arr[10];
xytype &rxy = arr[2]; //rxy a reference
}
C++11后置返回类型(另一种声明语法auto) 看下方伪代码:
template
?type? gt(T1 x, T2 y)
{
return x + y;
}
无法预先知道将x和y相加得到的类型,函数返回值类型出还未声明参数x和y,它们不在作用域内,无法使用decltype(x+y)。此时可以使用auto,auto个占位符,表示后置返回类型提供的类型。上述代码修改如下:
template
auto gt(T1 x, T2 y)->decltype(x+y)
{
return x + y;
}
现在,decltype在参数声明后面,因此x和y位于作用域内。