C++ 模板
前言
第 1 部分 基础概念
本部分将会介绍 C++ 模板的一些基础概念和语言特性。将会通过函数模板和类模板的例子来讨论模板的目的和概念。然后会继续介绍一些其他的模板特性,比如非类型模板参数(nontype template parameters),变参模板(variadic templates),typename 关键字和成员模板(member templates)。也会讨论如何处理移动语义(move semantics),如何声明模板参数,以及如何使用泛型代码实现可以在编译阶段执行的程序(compile-time programming)。在结尾处我们会针对一些术语和模板在实际中的应用,给应用开发工程师和泛型库的开发者们提供一些建议。
为什么要使用模板?
C++ 要求我们要用特定的类型来声明变量,函数以及其他一些内容。这样很多代码可能就只是处理的变量类型有所不同。比如对不同的数据类型,快速排序的算法实现在结构上可能完全一样,不管是对整型的数组,还是字符串类型的向量(vector),只要他们所包含的内容之间可以相互比较。
如果你所使用的语言不支持这一泛型特性,你将可能只有如下糟糕的选择:
- 你可以对不同的类型一遍又一遍的实现相同的算法。
- 你可以在某一个公共基类(common base type, 比如 Object 和 void*)里面实现通用的算法代码。
- 你也可以使用特殊的预处理方法。
如果你是从其它语言转向 C++,你可能已经使用过以上几种或全部的方法了。然而他们都各有各的缺点:
- 如果你一遍又一遍地实现相同算法,你就是在重复地制造轮子!你会犯相同的错误,而且为了避免犯更多的错误,你也不会倾向于使用复杂但是很高效的算法。
- 如果在公共基类里实现统一的代码,就等于放弃了类型检查的好处。而且,有时候某些类必须要从某些特殊的基类派生出来,这会进一步增加维护代码的复杂度。
- 如果采用预处理的方式,你需要实现一些“愚蠢的文本替换机制”,这将很难兼顾作用域和类型检查,因此也就更容易引发奇怪的语义错误。
而模板这一方案就不会有这些问题。模板是为了一种或者多种未明确定义的类型而定义的函数或者类。在使用模板时,需要显式地或者隐式地指定模板参数。由于模板是 C++ 的语言特性,类型和作用域检查将依然得到支持。
目前模板正在被广泛使用。比如在 C++ 标准库中,几乎所有的代码都用到了模板。标准库提供了一些排序算法来排序某种特定类型的值或者对象,也提供类一些数据结构(亦称容器)来维护某种特定类型的元素,对于字符串而言,这一“特定类型”指的就是“字符”。当然这只是最基础的功能。模板还允许我们参数化函数或者类的行为,优化代码以及参数化其他信息。这些高级特性会在后面某些章节介绍,我们接下来将先从一些简单模板开始介绍。
第 1 章 函数模板
第 2 章 类模板
第 3 章 非类型模板参数
第 4 章 可变参数模板
第 5 章 基础技术
第 6 章 移动语义与 enable_if()
第 7 章 按值传递还是按引用传递?
第 8 章 编译期编程
第 9 章 在实战中使用模板
第 10 章 基础模板术语
第 11 章 泛型库
第 2 部分 深入模板
本书的第一部分提供了基于 C++ 模板的大部分语法概念的解读,这足以对日常 C++ 编程所能遇到的问题进行解答。本书的第二部分提供了一个参考,旨在回答在推动语言发展过程中实现某些高级软件效果时遇到的更不寻常的问题。如果需要,您可以在初读时跳过此部分,并根据后面各章中的引用提示或在索引中查找某个概念时返回特定主题。
我们的目标是在清楚和完整的前提下,保持讨论的简洁性。为此,示例都很简短,通常也有些虚假。这也确保了我们不会错开手头的话题而陷入到无关的议题中。
此外,我们还前瞻了 C++ 模板语言特性未来的变化与扩展。这一部分的议题包含以下内容:
- 基础模板声明议题
- 模板中名称的意义
- C++ 模板实例化机制
- 模板参数推导规则
- 特化与重载
- 未来的可能性