目录
- **前言**
- **1. 推导规则**
- **2. 不能使用 auto 的场景**
- **3. 常见的使用场景**
前言
在 C++11 以前,auto 关键字的语义继承自 C 语言,表示 进入块后,自动分配内存,即分配堆栈内存。也就是说 auto 只能用于函数内,然而函数内部的声明变量默认使用的就是这种分配,因此在 C++11 以前,auto 关键字几乎没什么用处。
而在 C++11 以后,auto
关键字被赋予了新的语义:自动类型推导。
auto 与 decltype 作用类似,但有一定的区别
1. 推导规则
语法:
auto 变量名 = 变量值
auto 只是类型的 “占位符”,不是一个实际类型,它需要被编译器推导出来,因此:auto 声明的变量必须初始化,在编译时 auto 被真正的类型给替换掉。
推导规则满足:
当 表达式右部 的类型为
一般类型
(非指针、非引用):auto 就是对应的类型,但 顶层 const 被忽略如果你对 顶层与底层 const 不了解的,可见文章:【C++ const 详解】 的第 4 部分。
int a = 0; const int ca = a; ---------------------------------------- auto x = 0; // auto: int auto x = 1.0; // auto: double auto x = 'a'; // auto: char auto x = a; // auto: int auto x = ca; // auto: int (顶层 const 被忽略)
指针类型
:顶层 const 忽略,底层 const 不忽略,保留
指针类型int a = 0; int* p = 0; const int* cp = &a; int* const pc = &a; --------------------------------------- auto x = p; // auto: int* auto x = cp; // auto: const int* (底层 const 不忽略) auto x = pc; // auto: int* (顶层 const 忽略)
引用类型
:引用被忽略,const 不保留int a = 0; int& lr = a; int&& rr = 0; const int& clr = a; const int&& crr = 0; ----------------------------------- auto x = lr; // auto: int (引用被忽略) auto x = rr; // auto: int auto x = clr; // auto: int (引用被忽略,const 不保留) auto x = crr; // auto: int
volatile 关键字也满足上述规则中的 const 的规则
既然对于引用类型:引用 与 const 都被忽略,如果我们需要保留,那就需要手动指定。但是注意:auto 的推导仍然满足上面的推导过程。
【例】const auto& x = a; (int&& a = 0;)
【解】auto: int;故在编译时 auto 被替换为 int (auto 只是占位符),那么上述语句相当于:const int& x = a,因此 x 的类型为 const int&。
【注】
引用是 右值引用 时,需要注意引用折叠
问题,详情可见文章:【右值引用】 中的 4.1 部分。
也就是说,auto 在编译时先被编译器通过表达式右部的类型推导出来,并替换掉,而后 auto 修饰的变量的最终类型为 手动指定的类型 + auto 的实际类型。
但是 auto 并非时万能的,其中一个重要原因是:auto 是编译时推导。
2. 不能使用 auto 的场景
- 不能指定为 函数参数 的类型。
可以这么理解:auto 是再编译时推导的,但是函数形参需要在函数被调用时,也就是运行时,才能被确定
void fun(auto x); // 错误
- 对于类的成员,只能用于指定 非静态 const 成员 的类型。
class A { auto a = 0; // 错误 static auto b = 0; // 错误 static const auto c = 0; // 正确 };
- 无法定义数组
int arr[] = { 0, 1, 2 }; auto x = arr; // auto: int* auto x[] = arr; // 错误 auto x[] = { 0, 1, 2 }; // 错误
- 无法利用 auto 推导模板类型
template <typename T> class Test { }; Test<int> t; Test<auto> tt = t; // 错误:无法推导 auto 的类型
那么什么时候比较合适使用 auto 呢?
3. 常见的使用场景
- STL 容器的遍历
- 不使用 auto 遍历,那么迭代器的类型会比较长:
vector<int, vector<int>> t; vector<int, vector<int>>::iterator it = t.begin(); for (; it != t.end(); it++) {}
- 使用 auto 可以大大降低代码长度
vector<int, vector<int>> t; for (auto it = t.begin(); it != t.end(); it++) {}
- 模板中指定不确定函数的返回值类型
在模板编程中,有时会出现着这种情况,在不同类中,统一函数,但是返回值不同(这种设计有点不合常理,但是可能存在)class A { int fun(); }; class B { string fun(); }; // 两个 fun 返回值类型不同 // 模板函数 f 中可能会调用 A、B 中的 fun 函数 template <typename T> void f(T t) { // int x = t.fun(); // 还是 string x = t.fun(); // 如果指定为 int ;,那么当传入的 T = B 时,会报错:因为 B::fun() 返回值是 string // 那么可以如下指定: auto x = t.fun(); // ... ... }
- … …
它的使用场景有许多,这取决于编写代码者自己本身,但是需要注意:在符合 auto 使用规则的前提下,如果程序大片的使用 auto,这可能使得代码可读性很差。
本文如有错误,欢迎批评指正。