有时候程序中会出现难以拼写的复杂类型,或者是有时候程序不知道所需要的变量的类型是什么,C++提供了类型别名、auto类型说明符和decltype类型指示符以处理相应的情况。
类型别名
类型别名(type alias)是一个名字,是某种类型的同义词,它让复杂的类型名字变得简单易用。
有两种方法可以用于定义类型别名
- 传统方法,使用关键字typedef
- 新标准,使用别名声明,using
类型别名和类型的名字等价。但是如果某个类型别名指代的是复合类型或常量,可能会产生意想不到的结果。1
2
3
4
5
6
7typedef char *pstring;
char ps[] = "hello";
const pstring cstr = ps; // cstr是指向char的常量指针
const char *p = ps; // p是指向常量字符的指针
cstr[0] = 'J';
cout << ps << endl; // output Jello
p[0] = 'K'; // error! cannot change its content
auto类型说明符
有时候需要把一个较为复杂的表达式或者类型难以拼写的变量赋值给一个变量,C++11引入了auto类型说明符,可以让编译器分析表达式的值所属的类型,来推算变量的类型,显然,auto定义的变量必须有初始值。
1 | auto item = val1 + val2; // item初始化为val1和val2相加的结果,根据结果可以推断出item的类型 |
使用auto也能在一条语句中声明多个变量,但语句中所有变量的初始基本数据类型必须一样,即使是可以相互转换的类型也不行。
1 | auto i = 0, *p = &i; // 正确,i是int类型,p是指向int的指针 |
复合类型、常量和auto
编译器推断出来的auto类型有时和初始值的类型并不完全一样,编译器会适当改变结果类型使其更加符合初始化规则。
使用引用其实是使用引用的对象,因此引用作为初始值时,参与初始化的是引用对象的值,编译器以此为auto的类型。
1 | int i = 0, &r = i; |
其次,auto一般会忽略掉顶层const,底层const则会保留。
1 | const int ci = i, &cr = ci; // ci是const int类型,cr是const int&类型 |
如果希望推断出的auto类型是一个顶层const,需要明确指出(如上面例子中的cr):
1 | const auto f = ci; // ci的推演类型是int,f是const int |
可以将引用的类型设为auto,初始值中的顶层const仍然保留。
1 | auto & g = ci; // g是一个整型常量的引用 |
一条语句中定义多个变量时,符合&和*只从属于某个声明符,而非数据类型的一部分。
1 | auto k = ci, &l = i; // k是int,l是int & |
多维数组循环遍历中的auto
c++11新增了范围for语句,因此可以使用其遍历二维数组:
1 | int ia[][4] = {{1,2,3,4}, {5,6,7,8}}; |
除了最内层的循环外,外层循环的控制变量必须是引用类型,否则auto推断类型会把数组转为指针。
decltype类型指示符
有时候希望从一个表达式的类型推断出所需要定义的变量的类型,但又不想用该表达式的值初始化变量。c++11引入了类型说明符decltype,
它的作用是选择并返回操作数的数据类型。编译器并不计算表达式的值。
1 |
|
decltype处理顶层const和引用的方式与auto不同,如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用)。
1 | const int ci = 0, &cj = ci; |
其次,如果decltype使用的表达式不是一个变量,则返回该表达式结果对应的类型,如果表达式结果可赋值,则得到引用类型。
1 | int i = 42, *p = &i, &r=i; |
如果表达式的结果是左值类型,则返回一个引用,注意还有不可修改的左值——数组:
1 | int ia[][4] = {{1,2,3,4}, {5,6,7,8}}; |
对于变量,如果加上了括号,则被当作表达式,而变量是可赋值的特殊表达式,因此会返回引用类型。
1 | decltype(((i))) d; // int & |
另外与auto不同的地方是,当数组作为一个auto变量的初始值时,推断得到的类型是指针,而decltype关键字不会发生上述转换:
1 | int ia[] = {1,2,3,4}; |