gcc 预编译的奇怪行为 headers

Weird Behavior with gcc precompiled headers

我在让 pre-compiled header 工作时遇到了麻烦,所以我想出了以下 minimal-working-example.

这是 header 文件 foo.h

#include <iostream>
using namespace std;

void hello() {
    cout << "Hello World" << endl;
}

我将其编译为 g++ -c foo.h 给我编译的 header foo.gch。我希望当我编译以下包含 foo.h 的源文件时,它应该选择 header foo.h.gch 我很好。

// test.cpp
#include <cstdio>   // Swap ordering later
#include "foo.h"    // ------------------

int main() {
     hello();
}

但令人惊讶的是,这不是使用 foo.h.gch 编译,而是使用 foo.h。要验证您可以将其编译为 g++ -H test.cpp

但是,如果我按如下方式更改包含的 header 文件的顺序:

// test.cpp
#include "foo.h"    // ------------------
#include <cstdio>   // Ordering swapped

int main() {
     hello();
}

现在,如果我使用 g++ -H test.cpp 编译,它会从 foo.h.gch 编译,哇!

所以我想知道这是 GCC 中的错误还是我们应该像那样使用 pre-compiled header?无论哪种情况,我都认为了解它很有用..

使用 GCC,预编译的 headers 工作 如果它们是 header,并且如果它们首先包含在内(没有任何先前的 header)。

This answer 更详细地解释了为什么会这样。 另见 GCC 文档的 Precompiled headers 章节,其中说:

  • Only one precompiled header can be used in a particular compilation.
  • A precompiled header can't be used once the first C token is seen.

顺便说一句,pre-compiling 一些大的 header(特别是在 C++ 中)可能不值得付出努力。 YMMV.

来自 GCC manual pages:

A precompiled header can't be used once the first C token is seen.

因此,在您的预编译 header 中包含 <cstdio> 或首先包含它都可以。

简而言之,预编译的 header 是这样工作的:

当您请求创建“.pch”文件时,编译器会照常处理源文件。在这样做的同时,它的内部结构(主要是名称表和所有相关数据)被填充。最后,它对这些内部结构进行快照并将其保存到“.pch”文件中。

稍后,在编译包含 header 且存在“.pch”文件的源文件时,编译器可以省略 header 文件的昂贵处理并加载 ready-for-use 取而代之的是 '.pch' 文件的快照。

显然,只有在以下情况下才能在不影响语义的情况下完成此操作:

  • 包含指令先于其他;
  • 编译器选项相同。

包含指令之前的任何内容都可以:

  • 向编译器的内部数据结构添加一些内容;
  • 影响header文件的处理;
  • 改变那里描述的实体之间的关系。

因此,在这种情况下,加载内部数据结构的快照将是错误的,因为无法保证它会使这些结构保持与 header 正常处理后相同的状态.