第一次接触到auto关键字是无意间看到一行代码简洁优雅地实现了STL容器的迭代器,再看看我写的一长串诸如vector<int>::iteratoritvector<int>::iterator it\dotsb就默默流下了没有技术的眼泪😂。这次想到要整理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推导四规则(仅作了解)

  1. 如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么的decltype(e)就是e所命名的实体的类型。此外,如果e是一个被重载的函数,则会导致编译错误。
  2. 否则 ,假设e的类型是T,如果e是一个将亡值,那么decltype(e)为T&&
  3. 否则,假设e的类型是T,如果e是一个左值(左值就是在赋值中可以放在赋值操作符两边的值,一切变量都是左值,但const变量是例外),那么decltype(e)为T&。
  4. 否则,假设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用法