3.12 本章小结

在本章中,我们介绍了C++11中共11个崭新特性。这些新特性都在着重于通用性的考量下,经过标准委员会反复揣摩而最终成型。

最为引人注目的就是右值引用。右值引用堪称是C++11中的一项重大的变革。这次的变革,是以暴露原本一直被C/C++掩盖得较好的左值右值关系为代价的。不过客观地讲,也是在程序员对C++性能的一再紧逼下,左值右值的概念才终于“露出真身”。相比于C++的其他概念,右值引用的理解会稍微复杂一些,但其目的却比较明确,就是实现所谓的移动语义。移动语义与在C++98中常见的拷贝语义在类的构造上采用了完全不同的方式。移动语义主要是将行将被释放的资源“偷”出来,作为行将构造的类型的资源。那么这势必就会跟变量生命期产生关系,跟右值、临时量打上交道。而最终,C++11中又采用了右值引用的方式使得移动构造函数能够有效地获得这些右值临时量,以使程序员能够完成行为良好的移动语义。通过这样的移动语义,库的实现者可以巧妙地将各种形如堆内存的资源放入对象中,而不必担心在诸如函数传递的过程中带来过大的资源释放、申请开销。此外,标准制定者还趁机利用了右值引用来实现了所谓的完美转发,从技术上讲,完美转发就是通过引用折叠规则和模板推导规则,使得转发函数在不损失任何数据属性的情况下,将数据完美地传递给其他函数。完美转发在标准库中被广泛地使用,也是泛型编程中一种很好的包装方式。

在C++11中,标准则又重新回答了最基本的问题——什么是简单的类型及什么是复杂的类型,即怎么才算得上是POD。C++11的POD的概念分为平凡的和标准布局两个概念。标准布局强调了类型的数据在排布上是简单的,比如可以通过memcpy拷贝的简单类型。而平凡的则强调了类型没有复杂的构造、析构或者多态等看起来“不平凡”的行为。了解POD实际为了解C++11中其他很多的概念奠定了基础。

另外一个新引入的改动则是列表初始化。相比于C++98中的赋值表达式和值初始化,列表初始化主要被实现为标准库中的initializer_list,使得用户不仅可以列表式地初始化内置类型、数组、STL容器,还可以对自定义类型进行列表初始化。这应该是C++标准中第一次出现与库实现结合得如此紧密的语言特性。而列表初始化相对于老式的初始化,对总是容易出错的类型收窄做了限制。总的说来,无论从使用的方便性还是安全性上讲,列表初始化都表现出了优良的特性,也是C++核心语言进步的一种体现。

两种新的构造函数的声明方式——继承构造函数和委派构造函数,则都使得程序员能够在编写构造函数中少写一些代码。前者我们将关注点放在了继承结构下使用using关键字将构造函数继承上,而后者,我们则把目光放在了单类型中多个构造函数间通过初始化列表的相互委托关系上。而用户自定义字面量将C++中的各种重载再一次强化。通过后缀,用户可以将程序中非内置的几乎所有的字面量据为己用,产生自己的类型。同样地,这会减少C++11代码的书写量。另外一个简化,则是using的“泛化”,using关键字已经能够像typedef一样定义别名,且其在模板中使用起来更佳。

此外,我们还介绍了能够避免意外的显式类型转换,以及能为类产生变长成员的非受限联合体。内联的名字空间则是用于库发布的一个特性,通过将子名字空间的名字导入父名字空间,用户可以方便地使用名字空间的名字。不过名字空间的内联也导致名字空间对名字封装的失效。因此通常情况下,这个特性都会结合宏一起使用。

最后我们还介绍了SFINAE规则的改进,通过这样的改进,C++11进一步提高了范型编程的能力。

读完这一章,相信读者已经能够体会到C++11的一些风格。通用手段为先,如果能够使用更为通用的方法解决的话,就不再劳烦语言核心进行繁复的更改(这在列表初始化上体现得非常明显)。而在C++11中,我们也发现范型编程的力量越来越强大,语言层面的更改,很多都是在支持解决泛型编程中出现的一些问题(比如完美转发、模板别名、SFINAE等)。如果熟悉了这些特性,那么必然程序代码会越写越少,性能也越来越好。