前两周我们从宏观的层面上重新认识了 C++,从今天开始,我们将进入一个新的“语言特性”单元,“下沉”到微观的层面去观察 C++,一起去见一些老朋友、新面孔,比如 const、exception、lambda。
这次要说的,就是 C++11 里引入的一个很重要的语言特性:自动类型推导。
自动类型推导
如果你有过一些 C++ 的编程经验,了解过 C++11,那就一定听说过“自动类型推导”(auto type deduction)。
它其实是一个非常“老”的特性,C++ 之父 Bjarne Stroustrup(B·S ) 早在 C++ 诞生之初就设计并实现了它,但因为与早期 C 语言的语义有冲突,所以被“雪藏”了近三十年。直到 C99 消除了兼容性问题,C++11 才让它再度登场亮相。
那为什么要重新引入这个“老特性”呢?为什么非要有“自动类型推导”呢?
我觉得,你可以先从字面上去理解,把这个词分解成三个部分:“自动”“类型”和“推导”。
“自动”就是让计算机去做,而不是人去做,相对的是“手动”。
“类型”指的是操作目标,出来的是编译阶段的类型,而不是数值。
“推导”就是演算、运算,把隐含的值给算出来。
好,我们来看一看“自动类型推导”之外的其他几种排列组合,通过对比的方式来帮你理解它。
像计算“a = 1 + 1”,你可以在写代码的时候直接填上 2,这就是“手动数值推导”。你也可以“偷懒”,只写上表达式,让电脑在运行时自己算,这就是“自动数值推导”。
“数值推导”对于人和计算机来说都不算什么难事,所以手动和自动的区别不大,只有快慢的差异。但“类型推导”就不同了。
因为 C++ 是一种静态强类型的语言,任何变量都要有一个确定的类型,否则就不能用。在“自动类型推导”出现之前,我们写代码时只能“手动推导”,也就是说,在声明变量的时候,必须要明确地给出类型。
这在变量类型简单的时候还好说,比如 int、double,但在泛型编程的时候,麻烦就来了。因为泛型编程里会有很多模板参数,有的类型还有内部子类型,一下子就把 C++ 原本简洁的类型体系给搞复杂了,这就迫使我们去和编译器“斗智斗勇”,只有写对了类型,编译器才会“放行”(编译通过)。
int i = 0; // 整数变量,类型很容易知道
double x = 1.0; // 浮点数变量,类型很容易知道
std::string str = "hello"; // 字符串变量,有了名字空间,麻烦了一点
std::map<int, std::string> m = // 关联数组,名字空间加模板参数,很麻烦
{
{1,"a"}, {2,"b"}}; // 使用初始化列表的形式
std::map<int, std::string>::const_iterator // 内