第一次接触到auto关键字是无意间看到一行代码简洁优雅地实现了STL容器的迭代器,再看看我写的一长串诸如就默默流下了没有技术的眼泪😂。这次想到要整理C++11新特性,就先来谈谈这个auto关键字以及与其功能相似的decltype关键字:
1. auto关键字
1.1 auto简介
在C++11标准中,auto关键字的功能为自动类型推断。auto关键字的功能其实并不难理解,简而言之,就是让编译器根据变量的初值去自动推导这个变量的类型。
auto现在成了一个类型的占位符,通知编译器去根据初始化代码推断所声明变量的真实类型。各种作用域内声明变量都可以用到它。例如,名空间中,程序块中,或是for循环的初始化语句中。例如以下代码:
auto i = 42; // i is an int
auto d=23.3; //d is a double
auto l = 42LL; // l is a long long
正如之前所说,auto最常见的用法是用来声明STL容器的迭代器。不得不说,正规的迭代器声明实在是太长了,例如:
using namespace std;
//...
vector<int> vec(3);
vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++){
//...
}
然而,如果我们使用auto关键字,代码会简洁很多:
using namespace std;
//...
vector<int> vec(3);
for(auto it=vec.begin();it!=vec.end();it++){
//...
}
1.2. auto效率
auto实际上实在编译时对变量进行了类型推导,所以不会对程序的运行效率造成不良影响。另外,auto并不会影响编译速度,因为编译时本来也要右侧推导然后判断与左侧是否匹配。
1.3. 注意事项
auto关键字虽然很简单,使用也很方便,但其在使用中仍有以下几点事项需要注意:
- auto声明的变量必须要初始化,否则编译器不能判断变量的类型。
- auto不能被声明为返回值,auto不能作为形参,auto不能被修饰为模板参数。但是,如果函数有一个尾随的返回类型时,auto是可以出现在函数声明中返回值位置。这种情况下,auto并不是告诉编译器去推断返回类型,而是指引编译器去函数的末端寻找返回值类型。下面给出一个经典的例子,函数的返回值类型就是operator+操作符作用在T1、T2类型变量上的返回值类型:
template <typename _Tx, typename _Ty>
auto compose(_Tx x, _Ty y)->decltype(_Tx + _Ty) //decltype用法见后文
{
return x + y;
}
auto v = compose(2, 3.14); // v's type is double
2. decltype关键字
其实我也是在查询auto关键字时,第一次接触到decltype关键字。编译时类型推导的出现主要是为了泛型编程,因为在非泛型编程中,我们的类型都是确定的,根本不需要再进行推导。而编译时类型推导,除了上面介绍的auto关键字,还有decltype关键字。以下对decltype的用法进行整理:
2.1 decltype用法
decltype与auto关键字一样,用于进行编译时类型推导,不过它与auto还是有一些区别的。decltype的类型推导并不是像auto一样是从变量声明的初始化表达式获得变量的类型,而是以一个普通表达式作为参数,返回该表达式的类型,而且decltype并不会对表达式进行求值。例如:
int i = 4;
decltype(i) a; //推导结果为int。a的类型为int。
using size_t = decltype(sizeof(0)); //using此处用于定义别名,sizeof(a)的返回值为size_t类型
同样地,decltype也可以用于声明STL容器的迭代器:
vector<int >vec;
typedef decltype(vec.begin()) vectype;
for (vectype i = vec.begin; i != vec.end(); i++)
{
//...
}
此外,在C++中,我们有时候会遇上一些匿名类型,而借助decltype,我们可以重新使用这个匿名的结构体:
struct
{
int d ;
doubel b;
}anon_s;
decltype(anon_s) as ;//定义了一个上面匿名的结构体
而decltype最大的用途是在泛型编程中结合auto,用于追踪函数的返回值类型。(参考auto中最后一个示例代码)。
2.2 decltype推导四规则(仅作了解)
- 如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么的decltype(e)就是e所命名的实体的类型。此外,如果e是一个被重载的函数,则会导致编译错误。
- 否则 ,假设e的类型是T,如果e是一个将亡值,那么decltype(e)为T&&
- 否则,假设e的类型是T,如果e是一个左值(左值就是在赋值中可以放在赋值操作符两边的值,一切变量都是左值,但const变量是例外),那么decltype(e)为T&。
- 否则,假设e的类型是T,则decltype(e)为T。
如下面的例子,仅仅为i加上了(),就导致类型推导结果的差异。这是因为,i是一个标记符表达式,根据推导规则1,类型被推导为int。而(i)为一个左值表达式,所以类型被推导为int&。
int i=10;
decltype(i) a; //a推导为int
decltype((i))b=i;//b推导为int&,必须为其初始化,否则编译错误
具体内容可参考:decltype用法