图书前言

玄之又玄,众妙之门

大家都知道,AI可以写代码了。你用过吗?感觉如何?

前不久,我在准备GPU训练营的试验程序时,确实用AI写了一些代码,既有传统的CPU端代码,也有更现代的GPU端代码。我用AI写代码的目的有两个,一是提高工作效率,二是亲身测试AI写代码的能力。

亲身测试一番之后,我有两个比较强烈的感受。第一个感受是对于比较简单的任务,AI确实可以写出质量不错的代码,不仅速度快,而且准确度很高,没有误拼等人类常犯的低级错误。第二个感受是,随着代码量的上升,AI写的代码也开始具有人类代码常有的问题,先是重复,啰嗦,然后是有bug(错误)。 

众所周知,AI领域吸引了大量的投资和优秀的人才,新的成果不断涌现。因此,我们比较难预测AI的代码能力在2年后会怎么样?在5年和10年后又会怎么样?

AI技术的发展速度难以预测,但是我觉得以下三个趋势是比较确定的。首先,AI技术确实会改变软件产业的格局,一些简单的软件开发任务将AI化,因为使用AI技术能大大提高编码的效率,不再需要那么多的程序员来写代码。第二,随着AI技术不断被应用到软件开发领域,软件的产量和软件的代码量都将随之上升。而且,AI产生的代码也是不完美并且存在瑕疵的。软件团队里将需要很多调试工程师来定位各种稀奇古怪的问题。第三,在追求高性能、高可靠性的某些领域里,仍需要优秀的人类程序员来编写极端精致的代码。就像在机器可以包饺子的今天,仍有某些饺子店使用人工包。

其实,不管我的预测是否对,一名好的程序员都应该不断锤炼自己的编码能力,提高技术水平,让自己写出的代码越来越好。

于是,可能有人问,我已经能写出很漂亮的代码,什么样的代码算是更好呢? 

的确,评价代码好坏的标准有很多。在我看来,第一个硬指标就是generic,也就是通用性。展开来说,很多代码都有的一个通病就是长相类似的代码有很多份,结构类似,但有差异,不完全相同。

我是信儒家的,但偶尔也会读一点道家的作品,一般是在睡前读,因为读道家的作品读着读着就昏昏欲睡了。为什么呢?因为道家的话一般都比较“虚空”。用时髦的话说,就是不接地气,难以琢磨。比如一句“道可道,非常道”就有很多种解释。

我对道家的这种态度持续了很多年,直到有一天,当我领悟了计算机世界的一系列经典案例和一个永恒规律后,我又看到了“玄之又玄,众妙之门”。这八个字归纳得太好了,说出我心中所有,笔下所无,改变了我对道家的态度。

什么是玄而又玄呢?传统的解释有很多种,对多数程序员来说,都不大好理解。

在我看来,玄就是抽象。玄之又玄,就是抽象了再抽象。

人类的大脑喜欢生动具体的东西,比如小孩子都喜欢听故事,无论是“小马过河”还是“后羿射日”都有具体的场景、“人”和物。长大了以后喜欢刷剧也是类似的原因。每部剧都在一个具体的时空中讲一个故事。没有哪部剧没有人物,只有“道可道,非常道”。

因此,做抽象是很难的事情。也因此,很多代码都是不够抽象的,今天需要int类型的max()函数,那么就写个int类型的;明天需要float类型的,就把int类型的复制一份,改成float类型的。日积月累,整个项目里就有很多长相类似的代码了。

如何提炼这样的代码,消除重复,把它们合众为一呢?

传统C++中的模板技术就是为解决这个问题而设计的,现代C++将其发扬光大, 去除约束,增加功能,使其成为现代C++语言的一大亮点。

我认识文波和荣华多年,他们都在C ++语言和编程技术领域耕耘多年,孜孜不倦,满怀深情。更加可贵的是,他们把热爱转化为实际的行动,以各种形式推动技术的传播和发展。他们在翻译《C++模板》(第2版)之后,又将另一本模板编程的好书《C++20模板元编程》翻译成中文,功莫大焉。

 

张银奎

《软件调试》和《软件简史》的作者

译 者 序

C++的演化与模板的历史

自1979年Bjarne Stroustrup 创建C++以来,这门语言经历了多个重要的标准化版本,每一次演进都带来了新的特性和改进。从C++98的标准化到C++11迎来现代C++编程范式,再到C++14、C++17的稳定和扩展,现在C++20作为一个里程碑式的更新,引入了概念(Concepts)、范围(Ranges)、协程(Coroutines)等强大特性。其中,C++20对模板系统的扩展和改进,使得泛型编程更加直观、高效。

C++模板的历史可以追溯到20世纪80年代后期,它最初是为了解决代码复用的问题。1998年的C++标准(C++98)正式引入了模板,随后在C++11中得到了重要增强,如变参模板(Variadic Templates)、模板别名(Template Aliases)等。C++17进一步引入了折叠表达式(Fold Expressions)和类模板实参推导(Class Template Argument Deduction,CTAD)。到了C++20,概念(Concepts)的加入使得模板的可读性、可维护性大幅提升。

C++模板的优势

C++是一门支持多种编程范式的语言,包括:

●过程式编程(Procedural Programming)——基于函数和过程的结构化编程。

●面向对象编程(OOP)——通过类、继承和多态实现模块化与复用。

●泛型编程(Generic Programming)——借助模板编写类型无关的代码,提高代码复用性和灵活性。

●函数式编程(Functional Programming)——使用不可变数据和高阶函数,提升代码可测试性和并发能力。

●元编程(Metaprogramming)——利用编译期计算优化程序,提高运行效率。

在这些范式中,模板技术是C++的核心特性,它赋予C++强大的泛型编程能力,使代码适用于多种数据类型,而不需要冗余编写。例如,标准模板库(STL)的容器(如 std::vector、std::map)和算法(如 std::sort、std::find)均依赖模板实现。

C++模板的主要优势包括:

●编译时多态(Compile-time Polymorphism)——相比运行时多态(如继承与虚函数),模板允许编译期进行类型推导和优化,从而提高执行效率。

●编译时计算(Compile-time Computation)——利用模板元编程(TMP),C++能在编译期执行计算,减少运行时开销。例如,std::integral_constant 和 std::conditional 可用于选择编译期代码路径。

●代码复用——模板减少了重复代码,提高了通用性。例如,std::enable_if 可用于 SFINAE(替换失败非错误),实现条件编译。

模板的强大使其在现代C++开发中占据重要地位,特别是在高性能计算、游戏开发、底层系统编程等领域。掌握模板不仅能提升代码质量,还能帮助程序员深入理解C++语言的底层机制。

C++程序员必备的技能

对于希望深入掌握C++的开发者而言,理解模板是进阶C++编程的必经之路。从泛型编程(Generic Programming)、模板元编程(Template Metaprogramming),到C++20 概念(Concepts),这些技术都在现代C++开发中占据了重要地位。

无论是编写高效的库函数,还是优化应用程序的性能,模板都是必不可少的工具。例如,在高性能计算(HPC)、游戏开发、底层系统编程等领域,模板能够提供无与伦比的灵活性和效率。掌握模板不仅能够提高代码质量,还能帮助程序员更深入地理解C++语言的底层机制。

模板技术的学习建议

模板技术属于编译期编程,在学习过程中,建议结合反汇编,并善用Cpp Insights 等工具来观察模板实例化和生成的代码。

要系统学习模板技术,仅靠一本书是不够的。推荐阅读以下书籍:

●《C++ Templates (第2版·中文版)》[C++ Templates: The Complete Guide, 2nd Edition, (美)David Vandevoorde、(德)Nicolai M. Josuttis、(美)Douglas Gregor著,何荣华、王文斌、张毅峰、杨文波译,人民邮电出版社]——经典的C++模板书籍,全面介绍了模板技术。

●《编程原本》[Elements of  Programming,(美)Alexnader Stepanov、(美) Paul McJones著,裘宗燕译,人民邮电出版社]和亚历山大的系列博文(https://www.stepanovpapers.com/)——进一步深入泛型编程。

●《C实战:核心技术与最佳实践》(吴咏炜著,人民邮电出版社)——现代C 最佳实践。

此外,学习模板技术不能只依赖理论,还需要实践。建议阅读并改造以下模板库:

1. fmt(std::format的实现)——不依赖领域知识,适合作为入门教材。

2. blaze高性能数学库——作者 Klaus Iglberger 是模板设计模式专家。

3. folly库——Andrei Alexandrescu 主导,适合学习模板元编程。

4. cutlass C++模板库——Nvidia 出品,适用于深度学习优化。

结语与感谢

C++模板的强大使得它成为现代C++开发的基石,而C++20的更新更是让模板变得更易用、更强大。本书的目标是帮助读者全面理解C++20模板的核心概念,并掌握如何在实际开发中高效地应用模板技术。希望本书能为你打开C++模板编程的大门,帮助你在C++领域更进一步。

本书的出版离不开各方的支持。我们衷心感谢 Bjarne Stroustrup教授,他的贡献不仅塑造了C++语言,也为全球开发者提供了深远的技术指导。

特别感谢清华大学出版社的编辑团队,他们在术语规范、技术表达及出版质量方面提供了宝贵的支持,付出了大量的辛勤工作,确保本书得以高质量呈现。我们也感谢C++社区的开发者们,你们的深入讨论与实践经验为我们提供了极大的启发。

尽管我们力求精确,但面对C++如此庞大而复杂的体系,难免仍有不足之处。我们诚恳欢迎读者通过出版社反馈意见,以便我们进一步完善后续的修订工作。希望本书能够帮助广大开发者更深入地理解C++,更高效地运用这门强大语言。

献给那些总是渴望学习更多的好奇心灵。

——Marius Bancila

贡 献 者

关于作者

Marius Bancila 是一位拥有20年经验的软件工程师,在业务应用程序开发和其他领域都有丰富的解决方案经验。他是Modern C++ Programming Cookbook和The Modern C++ Challenge的作者。他目前担任软件架构师,专注于微软技术,主要使用C++和C#开发桌面应用程序。他热衷于与他人分享技术专长,因此自2006年起一直被认定为微软C++ MVP,后来还获得了开发者技术领域的 MVP 称号。Marius 居住在罗马尼亚,活跃于各种在线社区。

关于审校者

Aleksei Goriachikh拥有超过8年的C++编程经验。2012年从俄罗斯的新西伯利亚国立大学获得数学硕士学位后,Aleksei曾参与一些计算数学和优化领域的研究项目、某CAD系统的几何内核开发,以及自动驾驶的多线程库开发。Aleksei 最近的专业兴趣是硅前建模。

前    言

几十年来,C++一直是世界上使用最广泛的编程语言之一。它的成功不仅仅归功于其提供的性能或者说它的易用性(许多人对此持不同意见),而更可能是由于它的多功能性。C++是一种通用的多范式编程语言,它融合了过程式、函数式和泛型编程。

泛型编程是一种编写代码的方式,例如函数和类等实体是按照稍后实例化的类型编写的。这些泛型实体仅在需要作为实参具化为特定类型时才会实例化,这些泛型实体在C++中称为模板。

元编程是一种编程技术,它使用模板(以及C++中的constexpr函数)在编译期生成代码,然后将其与剩余源代码合并以便编译最终程序。元编程意味着其输入或输出中至少有一个是类型。

正如《C++核心指南》(Bjarne Stroustrup和Herb Sutter维护的一份关于应该做什么和不应该做什么的文档)中所描述的那样,C++中的模板可谓声名狼藉。然而,它们使得泛型库成为可能,比如C++开发人员一直使用的C++标准库。无论你是自己编写模板,还是只使用他人编写的模板(比如标准容器或算法),模板都很可能是你日常编码的一部分。

本书旨在让读者对C++中可用的所有范围内的模板都有很好的理解(从基本语法到C++20中的概念),这是本书前两部分的重点内容。第Ⅲ部分会帮助你将新获得的知识付诸实践,并使用模板进行元编程。

本书适读人群

本书适合想要学习模板元编程的初学者、中级C++开发人员,以及希望快速掌握与模板相关的C++20新功能和各种惯用法和模式的高级C++开发人员。在开始阅读本书之前,必须具备基本的C++编程经验。

本书涵盖内容

第1章“模板简介”。通过几个简单的例子介绍了C++中模板元编程的概念,讨论了为什么我们需要模板以及模板的优缺点。

第2章“模板基础”。探讨了C++中所有形式的模板:函数模板、类模板、变量模板和别名模板。我们讨论了其中每一个的语法和它们如何工作的细节。此外,还讨论了模板实例化和特化的关键概念。

第3章“变参模板”。专门介绍了变参模板,即具有可变数量模板形参的模板。我们详细讨论了变参函数模板、变参类模板、变参别名模板和变参变量模板、形参包及其展开方式,以及帮助我们简化编写变参模板的折叠表达式。

第4章“高级模板概念”。对一系列高级模板概念进行了分组,比如依赖名称和名称查找、模板实参推导、模板递归、完美转发、泛型和模板lambda函数。通过了解这些主题,读者将能够极大地扩展他们可以阅读或编写的模板的种类。

第5章“类型特征和条件编译”。专门讨论类型特征。读者将了解类型特征、标准库提供的特征以及如何使用它们解决不同的问题。

第6章“概念和约束”。介绍了新的C++20机制,通过概念和约束定义模板实参的需求。你将了解指定约束的各种方法。此外,还概述了C++20标准概念库的内容。

第7章“模式和惯用法”。探讨了一系列独立的高级主题,即利用迄今为止学到的知识实现各种模式。我们探讨了静态多态、类型擦除、标签派发和模式的概念,比如奇异递归模板模式、表达式模板、混入和类型列表。

第8章“范围和算法”。专注于理解容器、迭代器和算法,它们是标准模板库的核心组件。你将在这里学习如何为其编写泛型容器和迭代器类型以及通用算法。

第9章“范围库”。探讨了新的C++20范围库及其关键特性,例如范围、范围适配器和约束算法。这些使我们能够编写更简单的代码来处理范围。此外,你还将在这里学习如何编写自己的范围适配器。

附录是一个简短的结语,提供了本书的总结。

问题答案包含了所有章节中习题的答案。

充分利用本书

要开始阅读本书,首先需要对C++编程语言有一些基本的了解。需要了解有关类、函数、运算符、函数重载、继承、虚函数等的语法和基础知识。不过,对模板知识不做要求,因为本书将从头开始教你一切。

本书中的所有代码示例都是跨平台的。这意味着你可以使用任何编译器来构建和运行它们。然而,尽管许多代码段适用于C++11编译器,但也有一些代码段需要兼容C++17或C++20的编译器。因此,建议你使用支持C++20的编译器版本,以便运行所有示例。书中的示例已使用MSVC 19.30 (Visual Studio 2022)、GCC 12.1/13和Clang 13/14进行了测试。如果你的机器上没有这样一个兼容C++20的编译器,可以试着网上下载一个。我们推荐以下几个方案:

●Compiler Explorer (https://godbolt.org/)

●Wandbox (https://wandbox.org/)

●C++ Insights (https://cppinsights.io/)

本书多次引用C++ Insights在线工具来分析编译器生成的代码。

如果你想检查编译器对不同版本的C++标准的支持,应该参考页面https://en.cppreference.com/w/cpp/compiler_support。

提及标准和延伸阅读

在本书中,我们会多次提到C++标准。此文件版权归国际标准化组织所有。官方的C++标准文档可以从这里购买:https://www.iso.org/standard/79358.html。但是,C++标准的多个草案以及相应源码可以在GitHub上免费获得,网址为https://github.com/ cplusplus/draft。可以在https://isocpp.org/ std/the-standard链接上找到有关C++标准的更多信息。

C++ Reference网站是C++开发人员的一个很好的在线资源,网址为https://en. cppreference.com/。它提供了直接派生自C++标准的C++语言的详尽文档。本书多次引用了C++参考中的内容。C++参考的内容是基于CC-BY-SA协议的,https://en.cppreference. com/w/Cppreference:Copyright/CC-BY-SA。

(在每一章的末尾,你会发现一个名为“延伸阅读”的部分,该部分包含一份用作参考书目的阅读材料清单,推荐阅读以加深对所介绍主题的理解。)

下载示例代码文件

可以从GitHub上下载本书的示例代码文件,网址为https://github.com/ PacktPublishing/Template-Metaprogramming-with-CPP。也可以扫描封底二维码下载。如果代码有更新,将会在GitHub仓库中更新它。

下载彩图

我们还提供了一个PDF文件,其中包含本书中使用的屏幕截图和图表的彩图。可以在此处下载:https://packt.link/Un8j5。也可以扫描封底二维码下载。

使用的约定

本书使用了一些文本约定。

文本中的代码:表示文本中的代码词汇、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟URL、用户输入和Twitter用户名/账号标识。这里有一个例子:“这个问题可以通过将init设置为依赖名称来解决。”

代码块的格式如下:

template<typenameT>

structparser:base_parser<T>

{

voidparse()

{

this->init(); // 正确

std::cout<<"parse\n";

}

};

按如下方式编写任意命令行的输入或输出:

fatal error:recursive template instantiation exceeded maximum depth of 1024

use -ftemplate-depth=N to increase recursive template instantiation depth

粗体:表示一个新术语、一个重要单词或你在屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。这里有一个例子:“容量为8,大小为0,头部和尾部都指向索引0。”

提示或重要说明

迭代器概念在第6章“概念与约束”中进行了简要讨论。

本书的参考文献和问题答案可扫描封底二维码下载。