4.2 auto类型推导

类别:所有人

4.2.1 静态类型、动态类型与类型推导

在编程语言的分类中,C/C++常被冠以“静态类型”的称号。而有的编程语言则号称是“动态类型”的,比如Python。通常情况下,“静”和“动”的区别非常直观。我们可以看看下面这段简单的Python代码:


name='world\n'

print'hello,'%name


这段代码是Python中的一个helloworld的实现。这里的代码使用了一个变量name,用于存储world这个字符串。接下来代码又使用print将'hello,'字符串及name变量一起打印出来。请读者忽略其他的细节,只注意一下变量name的使用方式。事实上,变量name在使用前没有进行过任何声明,而当程序员想使用时,可以拿来就用。

这种变量的使用方式显得非常随性,而在C/C++程序员的眼中,每个变量使用前必须定义几乎是天经地义的事,这样通常被视为编程语言中的“静态类型”的体现。而对于如Python、Perl、JavaScript等语言中变量不需要声明,而几乎“拿来就用”的变量使用方式,则被视为是编程语言中“动态类型”的体现。不过从技术上严格地讲,静态类型和动态类型的主要区别在于对变量进行类型检查的时间点。对于所谓的静态类型,类型检查主要发生在编译阶段;而对于动态类型,类型检查主要发生在运行阶段。形如Python等语言中变量“拿来就用”的特性,则需要归功于一个技术,即类型推导。

事实上,类型推导也可以用于静态类型的语言中。比如在上面的Python代码中,如果按照C/C++程序员的思考方式,world\n表达式应该返回一个临时的字符串,所以即使name没有进行声明,我们也能轻松地推导出name的类型应该是一个字符串类型。在C++11中,这个想法得到了实现。C++11中类型推导的实现的方式之一就是重定义了auto关键字。另外一个现实是decltype,关于decltype的实现,我们将在后面详细描述。

我们可以使用C++11的方式书写一下刚才的Python代码,如代码清单4-3所示。

代码清单4-3


include <iostream>

using namespace std;

int main(){

auto name="world.\n";

cout<<"hello,"<<name;

}

//编译选项:g++ -std=c++11 4-2-1.cpp


这里我们使用了auto关键字来要求编译器对变量name的类型进行自动推导。这里编译器根据它的初始化表达式的类型,推导出name的类型为char*。这样一来就达到了跟刚才的Python代码差不多的效果。

事实上,auto关键字在早期的C/C++标准中有着完全不同的含义。声明时使用auto修饰的变量,按照早期C/C++标准的解释,是具有自动存储期的局部变量。不过现实情况是该关键字几乎无人使用,因为一般函数内没有声明为static的变量总是具有自动存储期的局部变量。因此在C++11中,标准委员会决定赋予auto全新的含义,即auto不再是一个存储类型指示符(storage-class-specifier,如static、extern、thread_local等都是存储类型指示符),而是作为一个新的类型指示符(type-specifier,如int、float等都是类型指示符)来指示编译器,auto声明变量的类型必须由编译器在编译时期推导而得。

我们可以通过代码清单4-4所示的例子来了解一下auto类型推导的基本用法。

代码清单4-4


int main(){

double foo();

auto x=1;//x的类型为int

auto y=foo();//y的类型为double

struct m{int i;}str;

auto str1=str;//str1的类型是struct m

auto z;//无法推导,无法通过编译

z=x;

}

//编译选项:g++ -std=c++11 4-2-2.cpp


在代码清单4-4中,变量x被初始化为1,因为字面常量1的类型为const int,所以编译器推导出x的类型应该为int(这里const类型限制符被去掉了,后面会解释)。同理在变量y的定义中,auto类型的y被推导为double类型;而在auto str1的定义中,其类型被推导为struct m。

值得注意的是变量z,这里我们使用auto关键字来“声明”z,但不立即对其进行定义,此时编译器则会报错。这跟通过其他关键字(除去引用类型的关键字)先声明后定义变量的使用规则是不同的。auto声明的变量必须被初始化,以使编译器能够从其初始化表达式中推导出其类型。从这个意义上来讲,auto并非一种“类型”声明,而是一个类型声明时的“占位符”,编译器在编译时期会将auto替代为变量实际的类型。