target_precompile_headers 是 cmake 3.16+ 提供的原生预编译头机制,用于加速编译;但仅适用于稳定、大体积、被广泛包含的头文件,模板或频繁变动的头文件反而降低增量编译效率。

target_precompile_headers 是什么,为什么不是所有项目都适合开
target_precompile_headers 是 CMake 3.16+ 引入的原生预编译头(PCH)支持机制,它替代了早期靠 set_property 或手动设置 COMPILE_FLAGS 的方式。但它不是“开了就快”的银弹:C++ 模板、宏定义频繁变动的头文件(比如带 __LINE__ 或依赖构建配置的头)一旦放进 PCH,后续修改会导致整个 PCH 失效并重建,反而拖慢增量编译。只有稳定、体积大、被大量源文件包含的头(如 <vector></vector>、<string></string>、项目级公共 common.hpp)才值得放进。
怎么写 target_precompile_headers 才不报错
常见错误是路径写错或目标未正确关联。必须确保:
-
target_precompile_headers必须在add_executable或add_library之后调用,且目标名完全一致 - 头文件路径是相对于
CMAKE_CURRENT_SOURCE_DIR的,不能用绝对路径或${CMAKE_SOURCE_DIR}/xxx拼接(CMake 会静默忽略) - 只支持单个主头文件(如
stdafx.h或pch.hpp),不支持通配符或多个顶层头 - 该头文件本身必须存在,且内容可被独立编译(不能依赖未定义的宏或前置
#include)
正确示例:
add_library(mylib src/a.cpp src/b.cpp) target_precompile_headers(mylib PRIVATE "pch.hpp")
如果 pch.hpp 在 include/ 目录下,应写成 "include/pch.hpp",而不是 "${CMAKE_SOURCE_DIR}/include/pch.hpp"。
PRIVATE / PUBLIC / INTERFACE 对预编译行为的影响
这个参数决定 PCH 是否“传染”给依赖者,但实际影响比普通属性更隐蔽:
-
PRIVATE:仅本 target 的源文件使用 PCH,最常用也最安全 -
PUBLIC:本 target 及所有链接它的 target 都会启用该 PCH —— 但若链接方源码里没包含对应头(比如你用了PUBLIC "pch.hpp",但对方.cpp里没#include "pch.hpp"),MSVC 会报C1010: unexpected end of file while looking for precompiled header;Clang/GCC 则可能跳过 PCH 或报错 -
INTERFACE:仅对链接者生效,本 target 自身不启用 —— 几乎无实用场景,容易引发混乱
结论:99% 场景用 PRIVATE,除非你明确控制上下游代码结构且强制要求统一 PCH。
Windows(MSVC)和 Linux(Clang/GCC)的行为差异
MSVC 默认严格校验 PCH 包含顺序:第一个非注释行必须是 #include "pch.hpp"(或你指定的头),否则直接失败;而 Clang/GCC 更宽松,即使没显式包含也能尝试复用,但效果不稳定。因此:
- 跨平台项目中,不要假设 PCH 一定能自动注入 —— 必须在每个
.cpp文件顶部手动加#include "pch.hpp"(放在所有其他#include之前) - MSVC 下开启
/Zi(调试信息)时,PCH 调试体验较差;建议开发阶段关掉 PCH,发布构建再启用 - Clang 的
-include机制与 CMake 的target_precompile_headers并不完全等价,某些旧版 Clang(compile_commands.json 中是否出现-Xclang -include-pch类参数
预编译头真正起效的前提,是头文件内容足够“钝”——不随编译单元变化,也不依赖局部宏。一不小心把它变成构建系统的隐形瓶颈,比不用还麻烦。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











