一、类型别名
类型别名是一个名字,它是某种类型的同义词。它能让复杂的类型名字变得简单明了、易于理解和使用,还有助于程序员清楚的知道使用该类型的真实目的。
有两种方法定义类型别名:
1.使用关键字 typedef (传统方法)
2.使用别名生命 using (C++11 新标准规定)
typedef double wages, *p; // wages 是 double 的同义词,p 是 double* 的同义词
using base = double*; // base 是 double* 的同义词
wages pi = 3.14; //等价于 double pi = 3.14;
p pd = π //等价于 double* pd = π
base pb = pd; //等价于 double* pb = pd;
base p1, p2; // p1, p2 的数据类型都是 double*
从上面的例子可以看到,两种方法都能达到类型别名的效果,但要强调的是:将类型别名替换成本来的样子的理解是错误的!!因为如果是替换的话,那么最后的 p2 就应该是 double 类型,而非 double* 。
二、 auto 类型说明符
为了解决声明变量时,需要清楚知道表达式的类型的问题。C++11 新标准引入了 auto 类型说明符,它能够让编译器通过初始值(表达式)的类型来推算变量的类型。显然,auto 定义的变量必须有初始值,并且在该声明语句只能够一个数据类型,即该语句所有变量的初始数据类型必须一样。
auto i = 0; *p = &i; //正确,i 是 int, p 是 int*
auto sz = 0; pi = 3.14; //错误,sz 与 pi 的数据类型不同
int i = 0; &t = i; //注意1
auto p = t; // p 的值是 0, 类型是 int
p = 2; // p 的值改变, 其他的值都没变
const int ci = i, &cr = ci; //注意2, i 是指注意1 的
auto b = ci; // b 是 int 变量, 顶层const 已经忽略了, 可以修改b的值
auto c = cr; // c 和 b 是一样的
auto d = &i; // d 是普通指针
auto e = &ci; // e 是底层const, 不能通过 e 改变ci的值
const auto f = ci; // f 是顶层const
在这里有几点需要注意以下:
1. 当初始化的值是引用时,真正参与初始化的是其引用对象的值,因此 auto 推算出来的数据类型是其引用对象的数据类型,而不是引用。如果希望推断出来的是引用要在 auto 后加上 &。
2. auto 会忽略顶层 const,同时底层 const 却会保留下来。如果希望推断出来的是顶层const,需要明确指出。
三、 decltype类型指示符
有时候我们希望从表达式推断出要定义的变量的类型,但又不想用该表达式的值初始化变量。C++11 新标准又引入了第二种类型说明符 decltype ,它的作用是选择并返回操作数的数据类型。
decltype(f()) sum; // sum 的类型是函数 f 的返回类型, 并不会实际调用
const int ci = 0; &cj = ci, *cp = &ci;
decltype(ci) x = 0; // x 是 const int, 顶层const
decltype(cj) y = x; // y 是 const int&, y绑定到x上
decltype((ci)) z; //错误, z 是 const int&, 必须初始化
decltype(*cp) p; //错误, p 是 const int&, 必须初始化
这里有几点与 auto 有所不同,需要注意一下:
1. 当表达式结果的数据类型是顶层 const 或 引用时,decltype 返回的就是顶层 const 或 引用类型,所以例子中的 x 和 y 依然保留顶层 const ,y 也是 引用类型。
2. 如果表达式的内容是解引用操作,则 decltype 将得到引用类型。所以 p ,其类型就是引用。
3. 如果表达式是一个变量,若变量名加上一对括号,则 decltype 将得到引用类型;若没有括号,则得到该变量的类型。所以例子中的 x 和 z 中,x 不是引用类型,但 z 是引用类型。