目录
一.简介
1.基本概念
在 C++11 中,auto
关键字被赋予了新的语义。在旧标准里,auto
用于声明自动存储期的变量,不过使用得较少且意义不大。而在 C++11 及以后,auto
主要用于自动类型推导。它允许编译器根据变量的初始化表达式自动推断出变量的类型,从而简化代码编写,尤其是在处理复杂类型时,能让代码更加简洁易读。
2.语法
auto 变量名 = 变量值;
编译器会根据 初始化表达式
的类型来确定 变量名
的类型。auto
本身并非一种实际的类型,而是一个类型占位符,它会在编译时被替换为具体的类型。
二.使用示例
示例1:基本类型推导
#include <iostream>
int main() {
auto num = 10; // 编译器根据 10 推断 num 的类型为 int
auto pi = 3.14; // 编译器根据 3.14 推断 pi 的类型为 double
std::cout << "Type of num: " << typeid(num).name() << std::endl;
std::cout << "Type of pi: " << typeid(pi).name() << std::endl;
return 0;
}
在这个例子中,num
被初始化为整数 10
,编译器推断其类型为 int
;pi
被初始化为浮点数 3.14
,编译器推断其类型为 double
。
示例2:带const 、volatile限定符
当变量不是指针或者引用类型时,推导的结果中不会保留volatile、const限定符
当变量是指针或者引用类型时,推导的结果中会保留volatile、const限定符
#include <iostream>
int main()
{
//double
auto x = 3.14;
//int
auto y = 520;
//char
auto z = 'a';
int temp = 100;
//int
auto* a = &temp;
//int*
auto b = &temp;
//int
auto& c = temp; //取别名,指向同一个内存地址
//int
auto d = temp;
//有const修饰
int tmp = 250;
//a1 : const int,auto:int
const auto a1 = tmp;
//当变量不是指针或者引用类型时,推导的结果中不会保留volatile、const限定符
//a2:int
auto a2 = a1;
//a3 : const int
const auto& a3 = tmp;
//当变量是指针或者引用类型时,推导的结果中会保留volatile、const限定符
//a4 : const int
auto& a4 = a3;
//pt4 : 保留,const int , 不能修改值
auto* pt4 = &a1;
return 0;
}
代码中注释了推导类型和规则。
示例3:迭代器类型推导(常用)
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 使用 auto 简化迭代器类型的书写
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
在 C++ 中,STL容器的迭代器类型通常比较复杂,使用 auto
可以避免手动书写冗长的迭代器类型,使代码更加简洁。
示例4:函数返回值类型推导
#include <iostream>
auto add(int a, int b) {
return a + b;
}
int main() {
auto result = add(3, 5);
std::cout << "Result: " << result << std::endl;
return 0;
}
这里 add
函数使用 auto
作为返回值类型,编译器会根据 return
语句中的表达式 a + b
推断返回值类型为 int
。
示例5:用于 Lambda 表达式
#include <iostream>
int main() {
auto lambda = [](int x, int y) { return x + y; };
int sum = lambda(2, 3);
std::cout << "Sum: " << sum << std::endl;
return 0;
}
Lambda 表达式的类型是编译器自动生成的匿名类型,使用 auto
可以方便地存储和使用 Lambda 表达式。
三.注意事项
1.函数形参,没有初始化
在函数形参列表中使用 auto
且没有初始化时是不被允许的。因为函数在定义时,编译器需要明确知道每个形参的具体类型,以便进行参数传递和类型检查等操作。而 auto
是依赖于初始化表达式来推导类型的,在函数形参这里没有合适的初始化过程来完成类型推导。
不过,在 C++14 及以后版本支持使用 auto
作为 lambda 表达式的形参类型,这是因为 lambda 表达式在调用时会根据传入的实参进行类型推导。
// 错误,普通函数形参不能使用 auto 且无初始化
// void func(auto param) {}
// C++14 及以后支持,lambda 表达式可使用 auto 形参
auto lambda = [](auto x) { return x * 2; };
2.不能用于类的非静态成员初始化,没有对象无法推导
非静态成员变量:类的非静态成员变量是与类的对象相关联的,每个对象都有自己独立的一份非静态成员变量。在类定义时,并没有实际的对象存在,所以编译器无法根据某个初始化表达式来推导 auto
的具体类型。
静态非常量成员变量:静态成员变量属于类本身,而不是某个具体的对象。类的静态非常量成员变量不允许在类内部直接初始化,需要在类外部进行定义和初始化,所以使用 auto
在这里也不合适,因为编译器无法在类定义阶段完成类型推导。
静态常量成员变量:静态常量成员变量可以在类内部直接初始化,并且使用 auto
时,如果初始化表达式是常量表达式,编译器可以在编译阶段完成类型推导,所以 static const auto
是可行的。
class T {
public:
auto v1 = 0; // 错误,非静态成员不能用 auto 初始化
static auto v2 = 0; // 错误,静态非常量成员不能在类内用 auto 初始化
static const auto v3 = 0; // 正确,静态常量成员可以用 auto 初始化
};
// 静态非常量成员需要在类外定义和初始化
// 如果 v2 定义为 static int v2; ,则需要在类外进行如下定义
// int T::v2 = 0;
3.不能使用 auto
关键字定义数组
使用数组初始化 auto
变量:当使用数组名初始化 auto
变量时,数组会退化为指针,所以 auto
会被推导为指针类型。例如 auto t1 = array;
中,t1
被推导为 int*
类型。
使用 auto
定义数组:使用 auto
直接定义数组是不允许的,因为编译器无法根据数组的初始化列表准确地推导出数组的类型和大小。
例如 auto t2[] = array;
和 auto t3[] = {1, 2, 3, 4, 5};
都是错误的。
int array[] = {1, 2, 3, 4, 5};
auto t1 = array; // 正确,t1 被推导为 int* 类型
// auto t2[] = array; // 错误
// auto t3[] = {1, 2, 3, 4, 5}; // 错误
4.无法使用 auto
推导出模板参数
模板参数在编译时需要明确的类型信息,而 auto
是在变量声明时根据初始化表达式进行类型推导的,它无法直接为模板参数提供明确的类型。模板实例化过程中,编译器需要确切知道模板参数的类型才能生成相应的代码。
template<typename T>
struct Test{
}
int func()
{
Test<double> t; //m模板实例化
Test<auto> t1 = t;//错误,无法推导出模板类型
return 0;
}
四.补充:decltype
1.推导规则:
1.+表达式为(1)普通变量或者(2)普通表达式或者(3)类表达式,在这种情况下,使用decltype推导出的类型和表达式的类型是一致的。
2.表达式时函数调用,使用decltype推导出的类型和函数返回类型一致。
纯右值:返回的时候要忽略const等限定符。
3.表达式是一个左值(可以取地址的变量),或者被括号包围,使用decltype推导出的是表达式类型的引用
如果有(const、volatile限定符不能忽略)
#include <iostream>
using namespace std;
class Test
{
public:
const int num = 9;
string tex;
};
int main()
{
int a=10;
decltype(a) b = 99;
decltype(a+3.14) c = 52.13;
decltype(a+b*c) d = 520.1314;
decltype(Test::num) e = 0;
return 0;
}
2.典型应用
泛型编程中的应用:
#include <iostream>
#include <list>
using namespace std;
template<class T>
class Container
{
public:
void print(T& t)
{
for(m_it=t.begin();m_it != t.end();++m_it)
{
cout << "value:" << *m_it << endl;
}
}
private:
//T() 表示调用类型 T 的默认构造函数来创建一个 T 类型的临时对象
decltype(T().begin()) m_it; //自动类型推导容器的迭代器类型
};
int main()
{
list<int> ls{1,2,3,4,5};
Container<list<int>> c;
Container<const list<int>> c2;
c2.print(ls);
return 0;
}
返回值类型后置:
语法:
auto func(参数1,参数2,...) ->decltype(参数表达式)
使用:
#include <iostream>
using namespace std;
//后置
//语法:auto func(参数1,参数2,...) ->decltype(参数表达式)
template<class T,class U>
auto add(T t, U u) ->decltype(t+u)
{
return t+u;
}
int main()
{
int x = 520;
int y = 13.14;
auto ret1 = add<int , double>(x,y);
auto ret2 = add(x,y);//自动类型推导
cout << "ret1:" << ret1 << endl;
cout << "ret2:" << ret2 << endl;
return 0;
}