为了充分利用本书,您应该已经了解 C++。我们描述特定语言功能的详细信息,而不是语言本身的基础知识。
您应该熟悉类和继承的概念,并且应该能够使用 C++ 标准库中的组件编写 C++ 程序,例如 IOstreams 和容器。
您还应该熟悉“现代C++”的基本特性,如auto
、decltype
、移动语义和lambda
表达式。
尽管如此,我们会在需要时审查更微妙的问题,即使这些问题与模板没有直接关系。这确保文本对专家和中级程序员都是可访问的。
我们主要涉及 2011 年、2014 年和 2017 年标准化的 C++ 语言修订版。然而,在撰写本文时,C++17 修订版刚刚完成, 我们预计大多数读者对其详细信息并不十分熟悉。所有修订对模板的行为和使用都产生了重大影响。因此, 我们对那些对我们的主题有最大影响的新特性进行简要介绍。然而,我们的目标既不是介绍现代 C++ 标准, 也不是对这一标准的之前版本([C++98]和[C++03])的更改进行详尽的描述。相反,我们专注于 C++ 中设计和使用的模板, 以现代C++标准([C++11]、[C++14]和[C++17])为基础,偶尔指出现代 C++ 标准鼓励使用与之前标准不同的技术的情况。
我们的目标,是提供开始使用模板并从其强大功能中受益所需的信息,同时为经验丰富的程序员提供使其推动技术极限的信息。 为了实现这一目标,我们决定将文本组织成几个部分:
- 第一部分介绍模板基础概念,采用教程风格编写;
- 第二部分介绍语言细节,是与模板相关构造的便捷参考;
- 第三部分解释由C++模板支持的基本设计和编码技术,涵盖从接近平凡的思想到复杂惯用法的范围;
每个部分都包含多个章节。此外,我们提供一些附录,涵盖与模板不专属相关的材料(例如,C++ 中的重载解析(overload resolution)概述)。 另一个附录涵盖了概念,这是对模板的基本扩展,已包含在未来标准的草案中(可能是C++20)。
第一部分的章节应按顺序阅读。例如,第 3 章建立在第 2 章的基础上。然而,在其他部分中,章节之间的连接相对较松散。 交叉引用将帮助读者跳转到不同的主题。
最后,我们提供了一个相当完整的索引,鼓励以非顺序方式阅读本书的其他方式。
如果您是一位希望学习或复习模板概念的 C++ 程序员,请仔细阅读第一部分《基础知识》。即使您已经对模板非常熟悉, 快速浏览这一部分可能有助于熟悉我们使用的风格和术语。该部分还涵盖了当源代码包含模板时组织代码的一些后勤方面。
根据您的首选学习方法,您可以决定在第二部分吸收许多模板的细节,或者您可以阅读第三部分关于实际编码技术的内容 (并在需要时参考第二部分,了解更微妙的语言问题)。如果您购买这本书是为了解决具体的日常挑战,后一种方法可能特别有用。
根据我们的经验,学习新知识的最佳方式是查看示例。因此,您将在整本书中找到很多例子。有些只是几行代码, 用来说明一个抽象概念,而另一些则是完整的程序,提供材料的具体应用。后一种类型的例子将由一个 C++ 注释引入, 描述包含程序代码的文件。您可以在本书网站 http://www.tmplbook.com 上找到这些文件。
C++ 程序员使用不同的编程风格,我们也是如此:关于空格的放置、分隔符(大括号、括号)等通常的问题会出现。 总体上,我们尽量保持一致,尽管偶尔会根据具体主题做出一些妥协。例如,在教程部分,我们可能更喜欢大量使用空 格和具体的名称,以帮助可视化代码,而在更高级的讨论中,更紧凑的风格可能更合适。
关于类型、参数和变量声明有一个稍微不同寻常的决定。显然,有多种样式可供选择:
void foo (const int &x);
void foo (const int& x);
void foo (int const &x);
void foo (int const& x);
虽然这有点不太常见,但我们决定使用顺序 int const
而不是 const int
来表示常量整数。
我们之所以选择这个顺序,有两个原因。首先,它使回答:什么是常量?这个问题更容易。
它总是在 const
限定符的前面。实际上,尽管
const int N = 100;
与
int const N = 100;
相同。但是
int* const bookmark;
却与
const int* bookmark;
或
int const* bookmark;
不相等。在这个例子中,int* const bookmark;
是指针本身是常量,而不是指向常量整数。
第二个原因与在处理模板时非常常见的一种语法替换原则有关。考虑以下两个使用 typedef
关键字的类型声明:
typedef char* CHARS;
typedef CHARS const CPTR; // const pointer to char
或使用 using
关键字:
using CHARS = char*;
using CPTR = CHARS const; // const pointer to char
当我们进行纯文本替换 CHARS
第二种声明时,其意义保持不变:
typedef char* const CPTR; // const pointer to char
或
using CPTR = char* const; // const pointer to char
然而,如果我们将 const
放在修饰的类型前,该原则就不适用了。考虑前面提到的两个类型定义的替代形式:
typedef char* CHARS;
typedef const CHARS CPTR;
在进行 CHARS
的文本替换后,CPTR
则拥有不同的意义:
typedef const char* CPTR; // pointer to const char
相同的情况也适用于 volatile
关键字。
关于空格,我们决定在与参数名之间加上空格:
void foo (int const& x);
通过这样做,我们强调了参数类型和参数名称之间的分隔。这的确会更加混淆以下这样的声明:
char* a, b;
在这里,根据从 C 继承的规则,a
是一个指针,而 b
是一个普通的 char
。
为了避免这种混淆,我们以这种方式声明多个实体。
这主要是一本关于语言特性的书。然而,许多技术、特性和辅助模板现在出现在 C++ 标准库中。
为了连接这两者,我们通过演示模板技术如何用于实现某些库组件来展示它们,并且我们使用标准库工具
来构建我们自己更复杂的示例。因此,我们不仅使用诸如 <iostream>
和 <string>
(其中包含模板但不特别相关于其他模板的定义)
这样的头文件,还使用 <cstddef>
、<utilities>
、<functional>
和 <type_traits>
(它们提供更复杂模板的基本构件)。
此外,我们提供了一个参考附录 D,介绍了 C++ 标准库提供的主要模板实用工具,包括对所有标准类型特征的详细描述。 这些在复杂模板编程的核心通常被广泛使用。
最初的 C++ 标准于 1998 年发布,随后在 2003 年由技术勘误进行了修正,对原始标准进行了一些微小的修正和澄清。 这个旧的 C++ 标准被称为 C++98 或 C++03。
C++11 标准是由 ISO C++ 标准化委员会推动的 C++ 的第一个重大修订,为语言引入了许多新特性。 其中一些新特性与模板交互,并在本书中进行了描述,包括:
- 可变参数模板(Variadic Templates)
- 别名模板(Alias Templates)
- 移动语义、右值引用和完美转发(Move semantics, Rvalue reference, Perfect forwarding)
- 标准类型特征(Standard type traits)
随后出台的 C++14 和 C++17 标准都引入了一些新的语言特性,尽管这些标准带来的变化并不像 C++11 那样显著。 与模板交互并在本书中描述的新特性包括但不限于:
- 变量模板(C++14)
- 泛型 Lambda(C++14)
- 类模板参数推导(C++17)
- 编译时
if
(C++17) - 折叠表达式(C++17)
我们甚至描述了概念(concepts
模板接口),目前计划在即将发布的 C++20 标准中包含。
在撰写本文时,主要编译器广泛支持 C++11 和 C++14 标准,C++17 也在很大程度上得到了支持。然而, 不同编译器在对不同语言特性的支持上仍存在很大差异。大多数编译器将能够编译本书中的大部分代码, 但有些编译器可能无法处理其中的一些示例。然而,我们预计随着全球程序员对其供应商提出标准支持的需求, 这个问题将很快得到解决。
即便如此,随着时间的推移,C++ 编程语言很可能会继续发展。C++社区的专家(无论他们是否参与C++标准化委员会: C++ Standardization Committee)正在讨论改进语言的各种方式,已经有几个候选的改进涉及到模板。 第 17 章介绍了这一领域的一些趋势。
您可以访问该书的网站以获取所有示例程序并查找更多相关信息。该网站的URL为:http://www.tmplbook.com。