预编译头(pch)仅在80%以上.cpp文件共用稳定大头文件时才有效;否则增加构建复杂度。启用需严格匹配编译参数、平台和标准,且pch内容须精简精准,避免宏污染与跨标准复用。

预编译头(PCH)到底该不该开
多数项目在启用预编译头后,首次编译变慢、后续编译才快——但如果你的代码频繁修改 stdafx.h 或 pch.h,它反而拖累整体迭代速度。是否启用,取决于头文件稳定性和团队协作模式。
判断依据很简单:项目里 80% 以上的 .cpp 文件是否都包含同一组不变的大头文件? 比如 <vector></vector>、<string></string>、<memory></memory>、<boost></boost> 这类不常改又很重的头。如果是,PCH 才真有用;否则只是徒增构建配置复杂度。
- Windows + MSVC:默认支持
pch.h/pch.cpp,但必须确保所有源文件顶部第一行是#include "pch.h"(不能有空行、注释或宏) - Linux/macOS + Clang/GCC:需手动用
-x c++-header生成pch.gch或pch.pch,且路径、语言标准(-std=c++17)必须和实际编译完全一致,否则静默失效 - CI 环境中容易漏掉 PCH 缓存清理,导致旧 PCH 被复用却链接失败——错误现象通常是
undefined reference to 'std::basic_string<...>::...' </...>这类符号缺失,而非编译报错
clang++ 和 g++ 的 PCH 生成命令差异
Clang 和 GCC 对预编译头的处理逻辑不同,直接套用对方命令会失败。关键不是“能不能生成”,而是“生成后能否被后续编译真正识别并加载”。
以 common.h 为例:
立即学习“C++免费学习笔记(深入)”;
clang++ -x c++-header -std=c++17 -I./include common.h -o common.h.pch
对应使用时加:
clang++ -include common.h -Xclang -include-pch -Xclang common.h.pch main.cpp
而 GCC 必须用 .gch 后缀,且不能显式指定加载:
g++ -x c++-header -std=c++17 -I./include common.h -o common.h.gch
然后编译时只需保证路径对、#include "common.h" 在最前,GCC 就自动匹配同目录下的 common.h.gch。多一个字母写成 common.h.pch,GCC 完全无视。
- Clang 的
-include-pch是强制加载,哪怕#include行不存在也生效;GCC 完全依赖文件名和位置匹配 - 两者都不支持跨语言标准复用:用
-std=c++20生成的 PCH,不能用于-std=c++17编译的源文件 - 若头文件里有
#ifdef __linux__这类平台宏,GCC 生成的 PCH 在 macOS 上必然失效——Clang 同理
为什么加了 PCH 反而更慢
常见原因是 PCH 本身成了瓶颈:体积过大、包含无关内容、或与实际编译参数不一致。预编译头不是“越多越好”,而是“越准越稳”。
典型症状:make -j4 下 CPU 占用低、单个 .cpp 编译耗时反而比关掉 PCH 还长。
- 检查 PCH 内容:删掉所有只在个别文件里用的头,比如
<opencv2></opencv2>或"network_client.h"—— 它们应该留在具体源文件里 - 避免在 PCH 中定义宏,尤其是影响模板实例化的(如
#define _HAS_AUTO_PTR_ETC 0),会导致 PCH 和源文件看到的类型不一致 - MSVC 下若开启
/Zi(生成调试信息),PCH 文件可能超 200MB,磁盘 I/O 成为瓶颈;改用/Z7并确保 SSD 存储 - Clang 的 PCH 加载过程会做 token 校验,若 PCH 是用
-O2生成、而源文件用-O0编译,部分优化相关宏行为不一致,也会退化为普通头处理
CMake 中正确集成 PCH 的要点
CMake 对 PCH 的支持是“半托管”:它能帮你生成和传递参数,但不会校验一致性。很多项目在升级 CMake 版本后 PCH 失效,就是因为 target_precompile_headers() 的行为变了。
CMake 3.16+ 推荐写法:
target_precompile_headers(mylib PRIVATE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/pch.h>"
)
注意:PRIVATE 表示只影响本 target 的编译,不传递给依赖者;若写成 PUBLIC,下游 target 可能意外继承,引发冲突。
- 不要在
add_compile_options()里硬塞-include,这会绕过 CMake 的 PCH 管理逻辑,导致 Ninja/Makefile 生成不一致 - 跨平台项目慎用
target_precompile_headers():CMake 在 MinGW 下默认禁用 PCH,即使写了也不会报错,而是静默忽略 - 如果项目含大量 header-only 库(如 Eigen、fmt),它们不该进 PCH——这些库本身设计为快速 include,预编译反而增加冗余解析
最麻烦的从来不是怎么配,而是怎么让所有人改完头文件后记得更新 PCH 列表,以及怎么在 git diff 里一眼看出某次提交是否破坏了 PCH 的稳定性。










