一、auto关键字介绍
因为C++是静态类型语言,即会在编译阶段进行类型检查。我们知道对象的类型决定一个对象所能参与的操作,因此如果类型检查过程中发现试图执行对象类型不支持的操作,编译器将会报错。
这要求我们在声明变量时必须指出变量的类型,而当用某个表达式初始化变量,有时我们根本不知道这个表达式的类型到底是什么,因此也就没办法给变量声明和定义。关于这一点,下面会给出例子。
1.同时定义一个变量
为了解决该问题,C++11引入了auto类型说明符,让编译器去分析表达式所属的类型。具体做法如下:
auto intVal=12;
12是一个int类型的字面值常量,因此auto推断intVal的类型为int。借助vscode查看变量类型可以证明这一点:
2.同时定义多个变量
另外要注意的一点是,如果在一个语句用auto声明了多个变量,则初始化这些变量的表达式都必须使对应声明的变量的类型一致,如:
auto intVal=12,doubleVal=0.12;
12是一个int类型的字面值常量,因此intVal判断出来的是int类型,而0.12是一个double类型的字面值常量,因此doubleVal推断出来的是double类型,两者推断的类型不一致,因此这条语句错误。证明如下:
3.抛出疑问
注意上面的红字,那为什么不说表达式类型相同,表达式类型相同不就使得推断的类型相同了吗?
是的,表达式类型相同,推断的类型是会相同。但表达式类型不同,有时也会使得推断的类型相同,继续看。
二、auto注意点
编译器推断出来的auto类型有时候和表达式初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则。如:
1.auto忽略引用
当初始值是一个引用时,auto推断的类型并不是引用,而是引用绑定的对象的类型:
int intVal=1;
int &refVal=intVal;
auto val=refVal;
借助vscode看到val的类型为int,而不是int&。
2.auto忽略顶层const
当初始值是一个顶层const时,auto推断的类型将不会是顶层const类型。
const int cIntVal=12;
auto val=cIntVal;
借助vscode看到val的类型为int,而不是const int。
如果希望val是一个顶层const,则必须自己手动添加const类型修饰符:
const int cIntVal=12;
auto const val=cIntVal;
借助vscode看到val的类型为const int。
3.auto不会忽略底层const
这一点比较好理解,代码如下:
const int intVal=12;
const int* intP1=&intVal;
auto intP2=intP1;
借助vscode看到val的类型为const int。
但需要注意的一点是,引用本身不是一个对象,因此常量引用其实是一个底层const,并不存在顶层const的说法,因此当auto去推断一个常量引用,同时声明的变量加了引用符(如果不加,推断的结果是引用绑定的对象去掉顶层const的类型),则推断的结果仍是常量引用,而不是普通引用,这符合C++不能使用非const引用绑定常量的语法规则。
const int intVal=12;
const int& ref1=intVal;
auto &ref2=ref1;
auto &ref3=12;
借助vscode看到ref2的类型为const int&。
4.auto推断数组名
数组名在C++很多场合下,都会转化为首元素地址,因此当auto推断的表达式是一个数组名的话,推断的结果是元素类型的指针。
int nums[3]={1,2,3};
auto p=nums;
借助vscode看到p的类型为int*。
三、返回疑问
表达式类型不同,有时也会使得推断的类型相同:
const int cVal=1;
auto intVal1=2,intVal2=cVal;
代码在vscode上,并没有报错:
四、尾后置返回类型
下面给出表达式类型未知的例子:
当我们使用C++模板写出下面的函数:
template<typename T, typename U>
? add(T a, U b)
{
return a + b;
}
请问a+b的类型是什么,我们根本没有办法确定a+b的类型。
因此为了应付这种情况,C++推出了尾后置返回类型,上面的函数正确的做法是:
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b)
{
return a + b;
}
该做法意思是,当函数体执行完,再来判断函数的返回类型。