1.auto
在 C++11 之前 auto 和 static 是对应的,表示变量是自动存储的,但是非 static 的局部变量默认都是自动存储的,因此这个关键字变得非常鸡肋。
- 在C++11 中他们赋予了新的含义,使用这个关键字能够像别的语言一样自动推导出变量的实际类型。
推导规则
- C++11 中 auto 并不代表一种实际的数据类型,只是一个类型声明的 “占位符”,auto 并不是万能的在任意场景下都能够推导出变量的实际类型,使用auto声明的变量必须要进行初始化,以让编译器推导出它的实际类型,在编译时将auto占位符替换为真正的类型。
- 使用语法如下:
auto 变量名 = 变量值;
- eg:
auto x = 3.14; // x 是浮点型 double
auto y = 520; // y 是整形 int
auto z = 'a'; // z 是字符型 char
auto nb; // error,变量必须要初始化
auto double nbl; // 语法错误, 不能修改数据类型
auto 还可以和指针、引用结合起来使用也可以带上 const、volatile 限定符,在不同的场景下有对应的推导规则,规则内容如下:
-
当变量不是指针或者引用类型时,推导的结果中不会保留 const、volatile 关键字
-
当变量是指针或者引用类型时,推导的结果中会保留 const、volatile 关键字
-
eg:一组变量带指针和引用并使用 auto 进行类型推导的例子
int temp = 110;
变量 a 的数据类型为 int*,因此 auto 关键字被推导为 int类型
auto *a = &temp;
变量 b 的数据类型为 int*,因此 auto 关键字被推导为 int* 类型
auto b = &temp;
变量 c 的数据类型为 int&,因此 auto 关键字被推导为 int类型
auto &c = temp;
变量 d 的数据类型为 int,因此 auto 关键字被推导为 int 类型
auto d = temp;
- eg:一组带 const 限定的变量,使用 auto 进行类型推导的例子
int tmp = 250;
变量 a1 的数据类型为 const int,因此 auto 关键字被推导为 int 类型
const auto a1 = tmp;
变量 a2 的数据类型为 int,但是 a2 没有声明为指针或引用因此 const 属性被去掉,auto 被推导为 int
auto a2 = a1;
变量 a3 的数据类型为 const int&,a3 被声明为引用,因此 const 属性被保留,auto 关键字被推导为 int 类型
const auto &a3 = tmp;
变量 a4 的数据类型为 const int&,a4 被声明为引用因此 const 属性被保留,auto 关键字被推导为 const int 类型
auto &a4 = a3;
2.auto 的限制
auto 关键字并不是万能的,在以下这些场景中是不能完成类型推导的:
- 不能作为函数参数使用。因为只有在函数调用的时候才会给函数参数传递实参,auto 要求必须要给修饰的变量赋值,因此二者矛盾。
int func(auto a, auto b) // error
{
cout << "a: " << a <<", b: " << b << endl;
}
- 不能用于类的非静态成员变量的初始化
class Test
{
auto v1 = 0; // error
static auto v2 = 0; // error,类的静态非常量成员不允许在类内部直接初始化
static const auto v3 = 10; // ok
}
- 不能使用 auto 关键字定义数组
int func()
{
int array[] = {1,2,3,4,5}; // 定义数组
auto t1 = array; // ok, t1被推导为 int* 类型
auto t2[] = array; // error, auto无法定义数组
auto t3[] = {1,2,3,4,5};; // error, auto无法定义数组
}
- 无法使用 auto 推导出模板参数
template <typename T>
struct Test{}
int func()
{
Test<double> t;
Test<auto> t1 = t; // error, 无法推导出模板类型
return 0;
}
3.auto 的应用
- 用于STL的容器遍历。
在 C++11 之前,定义了一个 stl 容器之后,遍历的时候常常会写出这样的代码:
#include <map>
int main()
{
map<int, string> person;
map<int, string>::iterator it = person.begin();
for (; it != person.end(); ++it)
{
// do something
}
return 0;
}
可以看到在定义迭代器变量 it 的时候代码是很长的,写起来就很麻烦,使用了 auto 之后,就变得清爽了不少:
#include <map>
int main()
{
map<int, string> person;
// 代码简化
for (auto it = person.begin(); it != person.end(); ++it)
{
// do something
}
return 0;
}
- 用于泛型编程,在使用模板的时候,很多情况下我们不知道变量应该定义为什么类型,比如下面的代码:
#include <iostream>
#include <string>
using namespace std;
class T1
{
public:
static int get()
{
return 10;
}
};
class T2
{
public:
static string get()
{
return "hello, world";
}
};
template <class A>
void func(void)
{
auto val = A::get();
cout << "val: " << val << endl;
}
int main()
{
func<T1>();
func<T2>();
return 0;
}
在这个例子中定义了泛型函数 func,在函数中调用了类 A 的静态方法 get () ,这个函数的返回值是不能确定的,
如果不使用 auto,就需要再定义一个模板参数,并且在外部调用时手动指定 get 的返回值类型,
具体代码如下:
#include <iostream>
#include <string>
using namespace std;
class T1
{
public:
static int get()
{
return 0;
}
};
class T2
{
public:
static string get()
{
return "hello, world";
}
};
template <class A, typename B> // 添加了模板参数 B
void func(void)
{
B val = A::get();
cout << "val: " << val << endl;
}
int main()
{
func<T1, int>(); // 手动指定返回值类型 -> int
func<T2, string>(); // 手动指定返回值类型 -> string
return 0;
}
-
测试:
-
参考:链接