Namespaces
Variants

Source file inclusion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

在当前源文件的指令所在行之后立即包含其他源文件。

目录

语法

#include < h-char-sequence > new-line (1)
#include " q-char-sequence " new-line (2)
#include pp-tokens new-line (3)
__has_include ( " q-char-sequence " )
__has_include ( < h-char-sequence > )
(4) (自 C++17 起)
__has_include ( string-literal )
__has_include ( < h-pp-tokens > )
(5) (自 C++17 起)
1) 搜索由 h-char-sequence 唯一标识的头文件,并将该指令替换为头文件的全部内容。
2) 搜索由 q-char-sequence 标识的源文件,并将该指令替换为源文件的全部内容。它可能回退到 (1) 的处理方式,将 q-char-sequence 视为头文件标识符。
3) 若既不符合 (1) 也不符合 (2) pp-tokens 将进行宏替换。替换后的指令会再次尝试与 (1) (2) 进行匹配。
4) 检查头文件或源文件是否可供包含。
5) (4) 未能匹配, h-pp-tokens 将进行宏替换。替换后的指令将再次尝试与 (4) 进行匹配。
new-line - 换行符
h-char-sequence - 一个或多个 h-char 组成的序列,其中以下字符的出现具有条件性支持且语义由实现定义:
  • 字符 '
  • 字符 "
  • 字符 \
  • 字符序列 //
  • 字符序列 /*
h-char - 源字符集 (C++23 前) 翻译字符集 (C++23 起) 中除换行符和 > 之外的任意成员
q-char-sequence - 一个或多个 q-char 组成的序列,其中以下字符的出现具有条件性支持且语义由实现定义:
  • 字符 '
  • 字符 \
  • 字符序列 //
  • 字符序列 /*
q-char - 源字符集 (C++23 前) 翻译字符集 (C++23 起) 中除换行符和 " 之外的任意成员
pp-tokens - 一个或多个 预处理记号 组成的序列
string-literal - 字符串字面量
h-pp-tokens - 一个或多个 预处理记号 组成的序列(不包括 >

说明

1) 在若干位置序列中搜索由 h-char-sequence 唯一标识的头文件,并将该指令替换为头文件的全部内容。具体如何指定搜索位置或标识头文件由实现定义。
2) 导致该指令被 q-char-sequence  所标识的源文件的全部内容所替换。命名源文件的搜索方式由实现定义。
若此搜索不被支持,或搜索失败,该指令将被重新处理,如同其语法为 (1) 且包含与原指令完全相同的序列(包括可能存在的 > 字符)。
3) 指令中 include 之后的预处理令牌会像普通文本一样被处理(即每个当前被定义为宏名称的标识符会被其预处理令牌的替换列表所替代)。
若所有替换后产生的指令不符合前述两种形式之一,则行为未定义。
位于 < > 预处理令牌对之间,或一对 " 字符之间的预处理令牌序列如何组合成单个头文件名预处理令牌的方法是由实现定义的。
4) h-char-sequence q-char-sequence 标识的头文件或源文件,其搜索方式如同该预处理令牌序列是语法 (3) 中的 pp-tokens ,但不再进行进一步的宏展开。
  • 若此类指令不满足 #include 指令的语法要求,则程序非良构。
  • 否则,若源文件搜索成功,则 __has_include 表达式求值为 1 ;若搜索失败,则求值为 0
5) 仅当语法 (4) 不匹配时才会考虑此形式,此时预处理标记的处理方式与普通文本完全相同。

如果由 头文件名 (即 < h-字符序列 > " q-字符序列 " )标识的头文件为可导入头文件,则 #include 预处理指令是否被替换为如下形式的 import指令 由实现定义:

import 头文件名 ; 新行

(自 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 #pragma once 具有类似效果:当同一文件(通过操作系统特定的方式确定文件标识)已被包含时,该指令会禁用对该文件的重复处理。

q-字符序列 h-字符序列 中,类似转义序列的字符序列可能导致错误、被解释为对应转义序列的字符,或具有完全不同的含义,具体取决于实现方式。

一个 __has_include 结果为 1 仅表示存在具有指定名称的头文件或源文件。这并不代表包含该头文件或源文件时不会引发错误,或其中包含任何有用内容。例如,在同时支持 C++14 和 C++17 模式的 C++ 实现中(并在其 C++14 模式下作为合规扩展提供 __has_include ), __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;
        }
        // 等等
    };
#endif
#include <iostream>
int main()
{
    if (has_optional > 0)
        std::cout << "<optional> is present\n";
    else if (has_optional < 0)
        std::cout << "<experimental/optional> is present\n";
    else
        std::cout << "<optional> is not present\n";
    optional_t<int> op;
    std::cout << "op = " << op.value_or(-1) << '\n';
    op = 42;
    std::cout << "op = " << op.value_or(-1) << '\n';
}

输出:

<optional> is present
op = -1
op = 42

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的C++标准。

DR 适用范围 已发布行为 正确行为
CWG 787 C++98 若在 q-字符序列 h-字符序列 中出现
类似转义序列的结构则行为未定义
改为有条件支持

参见

C++ 标准库头文件列表
C 文档 关于 源文件包含