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

时间:2016-11-15 21:09:07

标签: c++ gcc precompiled-headers

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

这是头文件foo.h

#include <iostream>
using namespace std;

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

我编译它,因为g++ -c foo.h给了我一个编译头foo.gch。我希望当我编译包含foo.h的以下源文件时,它应该选择标题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

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

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

int main() {
     hello();
}

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

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

3 个答案:

答案 0 :(得分:4)

使用GCC时,如果预编译头是标头,并且它们首先被包含在内(没有任何先前的标头),则预编译头只能

This answer解释了为什么会这样。 另请参阅GCC文档的Precompiled headers章节,其中说明了:

  
      
  • 在特定编译中只能使用一个预编译头。
  •   
  • 一旦看到第一个C令牌,就无法使用预编译的头。
  •   
BTW,可能会发生预编译某些大型标题(特别是在C ++中)并不值得付出努力。 YMMV。

答案 1 :(得分:4)

来自GCC manual pages

  

一旦看到第一个C令牌,就无法使用预编译的头。

因此,在预编译的标头中包含<cstdio>或首先包含它将会有效。

答案 2 :(得分:3)

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

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

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

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

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

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

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

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