源文件包含

出自cppreference.com


 
 
C++ 語言
 
 

將其他源文件包含到當前源文件中緊隨指令之後的一行。

語法

#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) 才會選擇此形式,此時預處理記號會按在正常文本中進行處理。

如果由標頭名(即 < h字符序列 >" q字符序列 ")標識的標頭指明了一個可以導入的標頭,那麼由實現決定要包含這個標頭的 #include 預處理指令是否要被替換為具有以下形式的導入指令

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 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字符序列 中包含了類似轉義序列的序列,那麼行為未定義 改為受條件性支持

參閱

C++ 標準庫頭文件列表
源文件包含C 文檔