9 标准库

9.1 引言

没有任何一款具有实用价值的软件是用“裸语言”写成的。人们通常会先开发一系列库,作为后续编程工作的基础。仅使用“裸语言”编写程序,注定是极其繁琐且乏味的,而善加利用事先编写好的库,编程工作会变得简单而高效。

C++标准库提供了众多工具和方法,它们基于一系列自定义的数据类型,如:string、ostream、variant、vector、map、path、unique_ptr、thread、regex、system_clock、time_zone、complex,等等。

在全面掌握标准库的所有细节之前,最好能对其中最常用的工具有一个基本了解。

在ISO C++的标准文本中,标准库规范占了将近三分之二的篇幅。在解决实际问题的过程中,应尽可能使用标准库的工具和方法,而不是自己定义功能相近的替代品。在标准库的设计与实现中,凝聚了大量多年积累并经过充分检验的思想。未来,还会有更多的人力物力,被投入到标准库的维护和扩展中。

标准库的工具和方法,固然是任何一个C++实现的必备部分。此外,大多数C++实现还提供了诸如图形用户界面(GUI)、网络编程、数据库访问等扩展功能。类似地,大多数基于C++语言的应用开发环境,还附带一套基础库,以满足企业级和工业级应用程序开发的需要。除此以外,还有成千上万的库,用于特定的专业领域,如流媒体、科学计算、机器视觉、人工智能等。当然,这些标准库以外库、系统和环境并不是这里关注的重点。这里讨论的内容,仅限C++标准定义的自包含描述,并确保其可移植性。在此基础上,进一步探索在大多数系统上可用的工具和方法,也是值得鼓励的。

9.2 标准库组件

标准库提供的工具和方法包括:

工具和方法说明
运行时支持动态内存管理、异常、运行时类型检查等
C标准库略有改动,以规避与类型系统的冲突
字符串国际字符集、本地化、子字符串只读视图等
正则表达式基于正则表达式的文本匹配
I/O流输入输出和格式化控制
文件以可移植的方式处理与具体文件系统无关的文件操作
容器和算法vector、map等容器和sort、find等算法,亦称标准模板库(STL)
范围视图、发生器、管道等
概念与基本数据类型和范围有关的各种概念
数值计算标准数学函数、向量计算、数学常数、随机数、复数
并发thread和锁机制
协程同步协程和异步协程
并行一部分数学算法和大多数泛型算法的并行版本
泛型工具模板元编程工具(如类型特性等)、泛型程序设计工具(如pair等)、
通用程序设计工具(如variant、optional等)
智能指针unique_ptr、shared_ptr等
特殊容器array、bitset、tuple等
绝对时间与时间段time_point、system_clock等
日历month、time_zone等
单位后缀表示毫秒的“ms”、表示虚数的“i”等
元素序列视图、string_view、span等

什么样的工具和方法才有资格入选标准库?

从本质上讲,C++标准库提供了最常用的基本数据结构,及针对这些数据结构的基础算法。

9.3 标准库的组织

标准库的所有设施都被置于std命名空间中。用户可以导入模块或包含头文件的方式,引用其中的组件。

9.3.1 命名空间

标准库中的每个工具和方法都有对应的头文件,例如:

包含这两个头文件后,在程序中就可以使用string和list了。

标准库组件位于std命名空间中。使用其中的工具和方法,需要加上“std::”前缀,例如:

如果觉得每次引用标准库组件都要使用“std::”前缀过于繁琐,也可以借助using指令,使位于std命名空间中的名字在当前作用域中可见,这时“std::”前缀可以省略不写。例如:

一般而言,将命名空间中的所有名字,都导入全局作用域,并不是一个好的编程习惯。但如果只使用std一个命名空间中的名字,这样做也无妨。

std命名空间中还有几个子命名空间:

std命名空间的子命名空间说明
std::chrono时间库
std::literals::chrono_literals时间后缀:y表示年、d表示日、h表示时、min表示分、s表示秒、ms表示毫秒、us表示微秒、ns表示纳秒
std::literals::complex_literals虚数后缀:i表示双精度虚数、if表示单精度虚数、il表示long long类型的虚数
std::literals::string_literals字符串后缀:s表示string类型的字符串
std::literals::string_view_literals字符串视图后缀:sv表示string_view类型的字符串视图
std::numbers数学常数
std::pmr多态内存资源

要使用子命名空间中的名字,必须将它们引入当前作用域。例如:

正确的写法是:

对于子命名空间中应该包含的内容,目前还缺乏统一连贯的理论指导。因此,为了避免歧义,使用后缀时,最好将相应的命名空间引入当前作用域。一个库如果定义了自用的后缀,通常需要将其定义在独立的命名空间中。

9.3.2 std::ranges命名空间

标准库提供的算法,如sort、copy等,有两个版本:

理想情况下,这两个版本应该基于参数表的差别,借助重载加以区分,但实际上却不可行。例如:

标准规定必须在作用域内显式指明范围版本的命名空间。例如:

9.3.3 模块

截至目前为止,还没有任何标准库模块,主要是因为C++标准化委员会缺乏时间,C++23可能会弥补这个遗漏。Microsoft Visual C++ 2022提供了基于C++23预览版的标准库模块——std。

9.3.4 头文件

下表罗列了部分标准库头文件,其中的声明都位于std命名空间中:

头文件主要内容
<algorithm>copy、find、sort
<array>array
<chrono>duration、time_point、month、time_zone
<cmath>sqrt、pow
<complex>complex、sqrt、pow
<concepts>floating_point、copyable、predicate、invocable
<filesystem>path
<format>format
<fstream>fstream、ifstream、ofstream
<functional>function、greater_equal、hash、range_value_t
<future>future、promise
<ios>hex、dec、scientific、fixed、defaultfloat
<iostream>istream、ostream、cin、cout
<map>map、multimap
<memory>unique_ptr、shared_ptr、allocator
<random>default_random_engine、normal_distribution
<ranges>sized_range、subrange、take、split、iterator_t
<regex>regex、smatch
<string>string、basic_string
<string_view>string_view
<set>set、multiset
<sstream>istringstream、ostringstream
<stdexcept>length_error、out_of_range、runtime_error
<tuple>tuple、get、tuple_size
<thread>thread
<unordered_map>unordered_map、unordered_multimap
<utility>move、swap、pair
<variant>variant
<vector>vector

此表远未囊括所有的标准库头文件。

C++标准库还提供了源自C标准库的头文件,如<stdlib.h>等。这类头文件都有一个对应的类似<cstdlib>的版本,其中的声明位于std命名空间中。

这些头文件反映了标准库的开发历程,因而并不象多数人所期待的那么富有逻辑,也不太易于记忆。这也是为什么需要一个std模块的原因。

9.4 建议