源文件包含 - cppreference.com
将其他源文件包含到当前源文件中紧随指令之后的一行。
语法
#include < h字符序列 > 换行
|
(1) | ||||||||
#include " q字符序列 " 换行
|
(2) | ||||||||
#include 记号序列 换行
|
(3) | ||||||||
__has_include ( " q字符序列 " )__has_include ( < h字符序列 > )
|
(4) | (C++17 起) | |||||||
__has_include ( 字符串字面量 )__has_include ( < h记号序列 > )
|
(5) | (C++17 起) | |||||||
1) 搜索由h字符序列 唯一识别的标头,并将该指令替换为这个标头的全部内容。
2) 搜索由q字符序列 识别的源文件,并将该指令替换为这个源文件的全部内容。可能退回至语法 (1) 并将q字符序列 视为标头标识符。
3) 如果 (1) 和 (2) 都不匹配,记号序列 会经历宏替换。该指令在替换后会再次尝试匹配 (1) 和 (2)。
4) 检查是否可以包含一个标头或源文件。
5) 如果 (4) 不匹配,h记号序列 会经历宏替换。该指令在替换后会再次尝试匹配 (4)。
| 换行 | - | 换行字符 |
| h字符序列 | - | 一个或多个h字符 的序列,并且其中以下内容的出现受条件性支持:
|
| h字符 | - | 源字符集(C++23 前)翻译字符集(C++23 起) 除了换行符和 > 以外的任何成员
|
| q字符序列 | - | 一个或多个q字符 的序列,并且其中以下内容的出现受条件性支持:
|
| q字符 | - | 源字符集(C++23 前)翻译字符集(C++23 起) 除了换行符和 " 以外的任何成员
|
| 记号序列 | - | 一个或多个预处理记号的序列 |
| 字符串字面量 | - | 一个字符串字面量 |
| h记号序列 | - | 一个或多个除了 > 以外的预处理记号的序列
|
解释
1) 在一系列地点中搜索由h字符序列 唯一识别的标头,并将该指令替换为这个标头的全部内容。由实现定义如何指定这些地点和识别标头。
2) 将该指令替换为由q字符序列 识别的源文件的全部内容。所指名的源文件通过由实现定义的方式进行搜索。
如果不支持这种搜索或者搜索失败,该指令按语法 (1) 重新处理,将原指令中包含的序列(包括 > 字符,如果存在)作为语法 (1) 中所需的序列。
3) 该指令中 include 后面的预处理记号会按在正常文本中进行处理(即每个目前定义为宏名的标识符都会被替换为它的预处理记号替换列表)。
如果在所有替换都完成后生成的指令不匹配前面两种语法,那么行为未定义(C++26 前)程序非良构,不要求诊断(C++26 起)。
将在一对预处理记号 < 和 > 之间或一对 " 字符之前的预处理记号序列合并为单个头名预处理记号的方法由实现定义。
4) 搜索由h字符序列 或q字符序列 识别的标头或源文件,如同通过将该预处理记号序列作为语法 (3) 中的记号序列 的方法进行搜索,但不会实施后续的宏展开。
- 如果刚才的语法 (3) 指令不满足
#include指令的语法要求,那么程序非良构。 - 否则
__has_include表达式在搜索成功时求值为1,在搜索失败时求值为0。
5) 只有在不匹配 (4) 才会选择此形式,此时预处理记号会按在正常文本中进行处理。
|
如果由标头名(即
|
(C++20 起) |
__has_include 可以在
#if 和
#elif 的表达式中展开。它被
#ifdef,
#ifndef,
#elifdef,
#elifndef(C++23 起) 和 defined 视为已定义的宏,但不能在其他任何地方使用。
注解
语法 (1) 的意图是搜索由实现所掌控的文件。典型实现仅搜索标准包含目录。这些标准包含目录中隐含地包含标准 C++ 库和标准 C 库。用户通常能通过编译器选项来控制标准包含目录。
语法 (2) 的意图是搜索不被实现所掌控的文件。典型实现首先于当前文件所在的目录搜索,然后退回使用语法 (1)。
包含一个文件时,它将经过翻译阶段 1-4 的处理,这可能递归地包含嵌套 #include 指令的展开,直到一个由实现定义的嵌套限制。为避免(可能传递性地)重复包含相同文件,和由文件包含自身造成的无限递归,通常使用头文件防护:整个头文件被包装在下列结构中
#ifndef FOO_H_INCLUDED /* 任何唯一地映射到文件名的名称 */ #define FOO_H_INCLUDED // 文件内容在此 #endif
许多编译器也会实现有类似效果的非标准语用 #pragma once:在已经包含相同文件(文件的身份以操作系统指定的方式确定)的时候禁止处理该文件。
如果h字符序列 或q字符序列 中包含了类似转义序列的序列,那么根据实现可能会导致错误,被处理为转义序列对应的字符,或者具有完全不同的含义。
结果是 1 的 __has_include 只表明存在有指定名称的头或源文件。它并不意味着包含该头或源文件时不会导致错误,或它会包含任何有意义的内容。例如在同时支持 C++14 和 C++17 模式(并在其 C++14 模式作为一项遵从标准的扩展而提供 __has_include)的 C++ 实现上,__has_include(<optional>) 在 C++14 模式中可以是 1,但实际上 #include <optional> 可能导致错误。
示例
#if __has_include(<optional>) #include <optional> #define has_optional 1 template<class T> using optional_t = std::optional<T>; #elif __has_include(<experimental/optional>) #include <experimental/optional> #define has_optional -1 template<class T> using optional_t = std::experimental::optional<T>; #else #define has_optional 0 template<class V> class optional_t { V v{}; bool has{}; public: optional_t() = default; optional_t(V&& v) : v(v), has{true} {} V value_or(V&& alt) const& { return has ? v : alt; } // etc. }; #endif #include <iostream> int main() { if (has_optional > 0) std::cout << "<optional> 存在\n"; else if (has_optional < 0) std::cout << "<experimental/optional> 存在\n"; else std::cout << "<optional> 不存在\n"; optional_t<int> op; std::cout << "op = " << op.value_or(-1) << '\n'; op = 42; std::cout << "op = " << op.value_or(-1) << '\n'; }
输出:
<optional> 存在 op = -1 op = 42
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
| 缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
|---|---|---|---|
| CWG 787 | C++98 | 如果h字符序列 或q字符序列 中包含了类似转义序列的序列,那么行为未定义 | 改为受条件性支持 |