使用gcc预编译头的奇怪行为

sud*_*03r 6 c++ gcc precompiled-headers

我遇到麻烦让预编译的头文件工作,所以我提出了以下最小工作示例.

这是头文件 foo.h

#include <iostream>
using namespace std;

void hello() {
    cout << "Hello World" << endl;
}
Run Code Online (Sandbox Code Playgroud)

我编译它g++ -c foo.h给我一个编译头foo.gch.我希望当我编译包含的以下源文件时foo.h,它应该选择标题foo.h.gch,我很好.

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

int main() {
     hello();
}
Run Code Online (Sandbox Code Playgroud)

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

但是,如果我更改包含的头文件的顺序如下:

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

int main() {
     hello();
}
Run Code Online (Sandbox Code Playgroud)

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

所以我想知道这是否是GCC中的错误,还是我们应该使用这样的预编译头文件?在任何一种情况下,我认为知道它是有用的..

Bas*_*tch 6

使用 GCC,预编译的头文件只有在它们是唯一的头文件并且首先包含它们(没有任何先前的头文件)时才起作用。

这个答案更多地解释了为什么会这样。另请参阅GCC 文档的Precompiled headers章节,其中说:

  • 在特定编译中只能使用一个预编译头。
  • 一旦看到第一个 C 标记,就不能使用预编译头。

顺便说一句,预编译一些大头文件(特别是在 C++ 中)可能不值得付出努力。天啊。


ach*_*ach 5

简而言之,预编译头的工作原理如下:

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

稍后,当编译包含存在“.pch”文件的标头的源文件时,编译器可以省略对标头文件的昂贵处理,而是从“.pch”文件加载随时可用的快照。

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

  • 包含指令优先于其他任何内容;
  • 编译器选项是相同的。

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

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

因此,在这种情况下,加载内部数据结构的快照将是错误的,因为不能保证它会使这些结构处于与标头正常处理后相同的状态。