auto的使用
在命名变量时通常需要清楚地知道表达式的类型,然而目前C++的变量类型越来越复杂,准确记忆变量的类型变得困难,auto的引入可以使编译器替我们去分析表达式所属的类型,编译器通过初始值来推算变量的类型。针对《C++ Primer》第五版中的讲解添加自己的一些注解,理理思路。
内置基本数据类型与auto
如下所示,根据字面值0的类型推断为int,所以i的类型是int,p的类型是int*。
//通过0推算auto为int,相当于int i = 0, *p = &i;
auto i = 0, *p = &i;
auto可以在一条语句中声明多个变量,但一条声明语句只能有一个基本数据类型,所以该语句中所有变量的初始基本数据类型必须一样,如下为错误示范。
//错误!!sz的推算类型为int,pi的推算类型为 double
auto sz = 0, pi = 3.14;
//错误!!如下所示,通过auto推算vector的大小类型时,想定义在此类型上的循环变量是错误的,编译器会根据a.size()和0分别推算,要确保类型一致
//在此例中,a.size()推算为long long unsigned int, 0推算为int,类型不一致。
std::vector<int> a{1, 2, 3, 4};
auto size = a.size(), b = 0;
复合类型(&、*)、常量与auto
使用引用真实目的是使用引用的对象,所以当引用被用作初始值时,编译器使用引用对象的类型作为auto的类型
//推算出a为int型
int i = 0, &r = i;
auto a = r;
用带有const属性的变量作为初始值时,auto会忽略掉顶层const,保留底层const
int i = 0, &r = i;
const int ci = i, &cr = ci;
auto b = ci; //b的类型为int,因为ci中const属性是顶层const,被忽略掉
auto c = cr; //c的类型为int,虽然cr是引用,const类型为底层const,但是如上所述,使用引用作为初始值时,编译器使用的是引用对象的类型
//所以这里相当于用auto c = ci;推算为int
auto d = &i; //d的类型为int*
auto e = &ci; //e的类型为const int*,指向常量的指针
//因为ci是顶层const,其取地址运算是一种底层const,所以auto保留下来底层const
如果希望推算出auto类型是一个顶层const,需要明确指出
int i = 0;
const int ci = i;
const auto f = ci; //f为const int型,ci推算为int,加上const即为f的类型
auto可以定义引用类型,设置一个类型为auto的引用时,初始值中的顶层const仍然保留
int i = 0;
const int ci = i;
auto &g = ci; //g是整型常量引用,绑定到ci,即const int &g = ci;顶层const保留
auto &h = 42; //错误!!不能为非常量引用绑定字面值,即int &h = 42;错误
const auto &j = 42; //正确,为常量引用绑定字面值
&和*只从属于声明符,并不是基本数据类型的一部分,故初始值必须是同一类型
int i = 0;
const int ci = i;
auto &m = ci, *p = &ci; //因为m是引用类型,ci中顶层const保留,推算为const int
//对ci取地址是一种底层const,保留,推算为const int
//两个初始值推算类型相同,正确,等价于const int &m = ci, *p = &ci;
内置数组与auto
数组有一个特性:在很多用到数组名字的地方,编译器会自动将其替换为指向数组首元素的指针。大多数表达式中,使用数组类型的对象其实是使用指向数组首元素的指针。因为,用数组作为auto变量的初始值时,推算出的类型是指针而不是数组。用数组初始化auto定义的引用时,推算类型仍然为数组。
int ia[] = {0, 1, 2, 3, 4, 5};
auto ia2 = ia, ia3(ia); //ia2和ia3的类型为int*,实际相当于auto ia2 = &ia[0], ia3(&ia[0]);
ia2[0] = 100;
std::cout << ia3[0] << std::endl; //输出100,利用指向数组的指针访问数组仍然可用
int arr[][4] = {1, 2, 3, 4, 5, 6, 7};
auto ia4(arr); //ia4的类型为int (*)[4],是一个指针,指向包含4个整型的数组
//用数组名做初始值替换为指向数组首元素的地址,在这里首元素为包含4个整型的数组,即auto ia4 = &arr[0];
auto ia5(arr[0]); //ia5的类型为int*,是一个指向整型的指针
//arr[0]是数组,则推算类型为指向数组首元素的指针
auto &ia6 = ia; //ia6的类型为int [6]
范围for与auto
C++11新增范围for用于遍历序列
int ia[2][3] = {{1}, {2}};
//错误做法!!无法通过编译,row被推算为int*,不能用作下一层的范围for
for (auto row : ia)
for (auto col : row)
std::cout << col << std::endl;
//正确做法!!row被推算为int [3],仍然是序列
for (auto &row : ia)
for (auto col : row)
std::cout << col << std::endl;
其他
关于auto和其他关键字(比如new)以及和模板、函数的组合,参考文章C++ 11新特性的用法之auto