C++ 标准库
C++ 标准库提供了可在标准 C++ 中使用的广泛设施。
目录 |
[编辑] 分类
语言支持库提供 C++ 语言某些部分所需的组件,例如内存分配(new/delete)和异常处理。
(C++20 起) |
诊断库提供了一个一致的框架,用于报告 C++ 程序中的错误,包括预定义的异常类。
内存管理库提供了用于内存管理的组件,包括智能指针和带域分配器(C++11 起)。
(C++11 起) |
通用工具库包括其他库元素使用的组件,例如用于动态存储管理的预定义存储分配器,以及在 C++ 程序中用作基础设施的组件,例如元组和(C++11 起)函数包装器。
容器、迭代器、范围(C++20 起)和算法库为 C++ 程序提供了访问最广泛使用的算法和数据结构子集的能力。
字符串库提供了对表示为以下类型同质序列的文本的操作支持:char、char8_t(C++20 起)、char16_t、char32_t(C++11 起)、wchar_t,以及任何其他类字符类型。
文本处理库提供了正则表达式匹配和搜索(C++11 起)、用于文本格式化的工具(C++20 起)和识别文本编码(C++26 起),以及本地化设施。
数值库提供了数值算法和复数组件,扩展了对数值处理的支持。valarray 组件支持 n-at-a-time 处理,在支持此类处理的平台上可能实现为并行操作。随机数组件提供了生成伪随机数的设施。(C++11 起)
时间库提供了通用的时间工具。
输入/输出库提供了iostream 组件,这是 C++ 程序输入和输出的主要机制。它们可以与库的其他元素一起使用,特别是字符串、本地化环境和迭代器。
(C++11 起) |
执行支持库提供了一个框架,用于在通用执行资源上管理异步执行。 |
(C++26 起) |
[编辑] 库内容
C++ 标准库为 C++ 标准库头文件的概要中所描述的实体和宏提供了定义,除非另有说明。
除了 operator new 和 operator delete 之外,所有库实体都定义在命名空间 std 或嵌套在命名空间 std 内的命名空间中(C 标准库设施的实体除外,见下文)。在一个特定命名空间中声明的名称是直接在该命名空间中声明,还是在该命名空间内的内联命名空间中声明,是未指定的。(C++11 起)
[编辑] 头文件
C++ 标准库的每个元素都在一个头文件中声明或定义(视情况而定)。头文件不一定是一个源文件,头文件名中由 <
和 >
分隔的序列也不一定是有效的源文件名。
C++ 标准库提供了C++ 库头文件和用于 C 库设施的附加 C++ 头文件(描述见“头文件”页面)。
用于 C 库设施的 C++ 头文件 | ||||
---|---|---|---|---|
<cassert> | <clocale> | <cstdarg> | <cstring> | |
<cctype> | <cmath> | <cstddef> | <ctime> | |
<cerrno> | <csetjmp> | <cstdio> | <cwchar> | |
<cfloat> | <csignal> | <cstdlib> | <cwctype> | |
<climits> | ||||
C++11 中增加的头文件 | ||||
<cfenv> | <cinttypes> | <cstdint> | <cuchar> | |
被移除的头文件 | ||||
<ccomplex> | (C++11 起)(C++17 中弃用)(C++20 中移除) | |||
<ciso646> | (C++20 中移除) | |||
<cstdalign> | (C++11 起)(C++17 中弃用)(C++20 中移除) | |||
<cstdbool> | (C++11 起)(C++17 中弃用)(C++20 中移除) | |||
<ctgmath> | (C++11 起)(C++17 中弃用)(C++20 中移除) |
自由实现有一组由实现定义的头文件,关于头文件集的最低要求见此处。
[编辑] C 标准库
C++ 标准库也提供了 C 标准库的设施,并经过适当调整以确保静态类型安全。许多库函数的描述依赖于 C 标准库来确定这些函数的语义。
在某些情况下,标准 C++ 中指定的签名可能与 C 标准库中的签名不同,并且可能声明了额外的重载,但行为和前提条件(包括 C 的 restrict所隐含的)(C++17 起)是相同的,除非另有说明。
为了与 C 标准库兼容,C++ 标准库提供了下面列出的 C 头文件。这些头文件的预期用途仅为互操作性。可能存在 C++ 源文件需要包含其中一个头文件才能成为有效的 ISO C 的情况。不打算同时成为有效 ISO C 的源文件不应使用任何 C 头文件。描述见此处。
C 头文件 | |||
---|---|---|---|
<assert.h> | <limits.h> | <stdarg.h> | <string.h> |
<ctype.h> | <locale.h> | <stddef.h> | <time.h> |
<errno.h> | <math.h> | <stdio.h> | <wchar.h> |
<float.h> | <setjmp.h> | <stdlib.h> | <wctype.h> |
<iso646.h> | <signal.h> | ||
C++11 中增加的头文件 | |||
<complex.h> | <inttypes.h> | <stdbool.h> | <tgmath.h> |
<fenv.h> | <stdalign.h> | <stdint.h> | <uchar.h> |
C++23 中增加的头文件 | |||
<stdatomic.h> | |||
C++26 中增加的头文件 | |||
<stdbit.h> | <stdchkint.h> |
除非另有说明,每个头文件 cxxx
的内容与 C 标准库中指定的相应头文件 xxx.h
的内容相同。然而,在 C++ 标准库中,声明(除了在 C 中定义为宏的名称)都位于命名空间 std 的作用域内。这些名称(包括任何添加的重载)是否首先在全局命名空间作用域内声明,然后通过显式的 using-declarations 注入到命名空间 std 中,是未指定的。
在 C 中定义为宏的名称(assert、offsetof、setjmp、va_arg、va_end 和 va_start)在 C++ 标准库中也必须定义为宏,即使 C 允许实现为函数。
在 C 中定义为函数的名称在 C++ 标准库中也必须定义为函数。这禁止了 C 中允许的做法,即在函数原型之外提供一个掩蔽宏。在 C++ 中实现等效内联行为的唯一方法是提供一个 extern inline 函数的定义。
在 C++ 中是关键字或运算符的标识符不能在 C++ 标准库头文件中定义为宏。特别是,包含标准头文件 <iso646.h> 没有效果。
[编辑] 与标准 C 中安全函数相关的名称 (C++17 起)
如果包含了任何 C++ 头文件,那么以下任何 C 标准附录 K 的名称是否在全局命名空间中声明是实现定义的(它们中没有一个在命名空间 std 中声明)。
[编辑] 使用库
[编辑] 包含头文件
C++ 标准库中的实体定义在头文件中,当翻译单元包含相应的 #include 预处理指令时,这些头文件的内容就可用于该翻译单元。
翻译单元可以以任何顺序包含库头文件。每个头文件可以被多次包含,其效果与只包含一次完全相同,但包含 <cassert> 或 <assert.h> 的效果每次都取决于 NDEBUG 的词法上当前定义。
翻译单元只能在任何声明或定义之外,并且在词法上先于该翻译单元中对该头文件中声明的任何实体的首次引用之前包含头文件。不需要诊断。
(C++20 起) |
导入头文件C++ 库头文件,或者对于自由实现,实现所提供的此类头文件的子集,统称为可导入的 C++ 库头文件。 当翻译单元包含相应的导入声明时,可导入的 C++ 库头文件的内容就可用于该翻译单元。 |
(C++20 起) |
导入模块C++ 标准库提供以下C++ 库模块
对于标准库中的每个声明, |
(C++23 起) |
[编辑] 链接
C++ 标准库中的实体具有外部链接。除非另有说明,对象和函数具有默认的 extern "C++" 链接。
从 C 标准库中声明的具有外部链接的名称,其链接是 extern "C" 还是 extern "C++",是实现定义的。C++ 标准建议在这种情况下使用 extern "C++"。
在库中定义并被 C++ 程序所需的对象和函数,在程序启动前会被包含到程序中。
[编辑] 对标准库实现的要求
[编辑] 保证
- 该头文件的概要中,或
- 另一个头文件的概要中,而该头文件似乎被包含在该头文件的概要中。
对于在多个头文件中定义的类型和宏(如 NULL),以任何顺序包含任意数量的这些头文件都不会违反单一定义规则。
除非另有说明,所有由 C 标准库定义的、展开为整型常量表达式的类对象宏都可以在 #if
预处理指令中使用。
调用一个标准库非成员函数签名总是会导致实际调用该函数。因此,一个符合标准的库实现不能定义可能被一个有效的 C++ 程序调用的额外的非成员函数。
非成员函数签名绝不会用额外的默认实参声明。
除非另有说明,标准库中的函数对非运算符、非成员函数的调用,不使用通过实参依赖查找找到的来自其他命名空间的函数。
对于类(模板)定义中的每个函数(模板)的友元声明,不会为该函数(模板)提供其他声明。
标准库函数签名只有在被要求为 constexpr 时才能声明为 constexpr(libstdc++ cmath 在这方面是明显不符合标准的)。如果一个头文件提供了 constexpr 函数或构造函数的任何非定义声明,那么相应的定义也应该在该头文件中提供。 除非另有说明,每个标准库函数应满足以下每个要求以防止数据竞争:
|
(C++11 起) |
对于 C++ 标准库中定义的每个要求从 C++ 标准库中定义的另一个类派生的类,
- 如果基类被指定为 virtual,那么它必须是虚基类,
- 如果基类没有被指定为 virtual,那么它不能是虚基类,并且
- 除非另有说明,具有不同名称的类型应为不同的类型。
除非另有说明,C++ 标准库中指定的所有类型都是非 final 类型。 |
(C++11 起) |
如果一个在 C++ 标准库中定义的函数被指定(在特定情况下)抛出一个给定类型的异常,那么抛出的异常只能是该类型或从该类型派生的类型,以便该基类型的异常处理器可以捕获它。
来自 C 标准库的函数只有在调用了抛出异常的程序提供的函数时才能抛出异常(qsort() 和 bsearch() 满足此条件)。
在 C++ 标准库中定义的析构函数操作永远不会抛出异常。C++ 标准库中的每个析构函数的行为都如同它有一个不抛出异常的异常规范。
如果 C++ 标准库中的函数通过 std::error_code 对象报告错误,那么对于源于操作系统的错误,该对象的 category() 成员必须返回 std::system_category();对于源于其他地方的错误,则必须返回一个由实现定义的 std::error_category 对象的引用。对于每种错误类别,value() 的可能取值都应当被定义。 C++ 标准库中定义的类型的对象可以被移出。移动操作可以是显式指定的,也可以是隐式生成的。除非另有规定,否则这样被移出的对象将处于一个有效的、但未指定的状态。 C++ 标准库中定义的类型的对象可以被移动赋值给自身。除非另有规定,否则这样的赋值会使对象进入一个有效的、但未指定的状态。 |
(C++11 起) |
[编辑] 实现自由度
C++ 标准库中的任何成员函数或非成员函数是否被定义为 inline 是未指定的。
对于一个非虚的 C++ 标准库成员函数,可以声明一组不同的成员函数签名,前提是任何会从该组声明中选择一个重载的对此成员函数的调用,其行为都如同那个重载被选中一样。例如,这允许:
- 添加带有默认实参的形参,
- 用两个或更多具有等价行为的成员函数替换一个带有默认实参的成员函数,或者
- 为一个成员函数名添加额外的签名。
除非另有规定,C++ 标准库中哪些函数可以被递归重入是由实现定义的。
C++ 标准库的实现可以在线程间共享其内部对象,只要这些对象对用户不可见且受到数据竞争的保护。 |
(C++11 起) |
C++ 标准库中的任何函数签名或类是否是 C++ 标准库中另一个类的友元是未指定的。
此处描述的名称和全局函数签名为实现保留。
C++ 标准库中的任何类都可以派生自一个名称为实现保留的类。如果 C++ 标准库中定义的某个类被要求派生自 C++ 标准库中的其他类,那么该类可以直接派生自所需的基类,或通过一个具有为实现保留名称的基类层次结构间接派生。
如果 C++ 标准库中定义的某个函数未被指定为会抛出异常,但没有非抛出异常规范,那么它所抛出的异常是由实现定义的,但其类型应该是 std::exception 或任何派生自 std::exception 的类型。
非虚函数的异常规范可以通过添加一个非抛出异常规范来加强。
[编辑] 注意
特性测试宏 | 值 | 标准 | 特性 |
---|---|---|---|
__cpp_lib_modules |
202207L |
(C++23) | 标准库模块 std 和 std.compat |
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
LWG 1 | C++98 | 来自 C 标准库的名称的 语言链接是未指定的 |
它们是 实现定义 |
LWG 119 | C++98 | 虚函数的异常规范 可以被加强 |
现仅允许用于 非虚函数 |
LWG 147 | C++98 | 关于非成员函数的规范 只考虑了全局函数 |
现也考虑 非全局函数 |
LWG 225 | C++98 | 标准库函数可能会调用非成员函数 由于实参依赖查找,可能来自其他命名空间 |
现被禁止,除非 另有规定 |
LWG 336 | C++98 | <strstream> 不是一个 C++ 库头文件 | 它是一个 C++ 库头文件 |
LWG 343 | C++98 | 库头文件的依赖关系未被指定 | 现已指定(在概要中列出) |
LWG 456 | C++98 | 用于 C 库设施的 C++ 头文件 只能在命名空间 std 中提供定义 |
现允许在全局命名空间中定义 然后注入到命名空间 std |
LWG 465 | C++98 | 在 C++ 中是关键字或运算符的标识符 可以在 C++ 标准库头文件中被定义为宏 (之前仅要求 <ciso646> 不将它们定义为宏) |
现在所有 C++ 标准 库头文件都不能 将它们定义为宏 |
LWG 1178 | C++98 | C++ 头文件必须包含一个包含 任何所需定义的 C++ 头文件 |
C++ 头文件必须提供在其概要中 直接或间接包含的声明 和定义 |
LWG 2013 | C++11 | 对于标准不要求为 constexpr 的函数, 标准库是否可以将其声明为 constexpr 是未指定的 |
已禁止 |
LWG 2225 | C++98 | 如果一个头文件在不正确的位置 被包含,则需要诊断信息 |
现在这种情况下 不需要诊断信息 |