使用大型预编译头的权衡问题及相关技术咨询
首先你的理解完全准确:预编译头本质就是编译器处理指定头文件后导出的状态快照,编译引用它的源文件时直接加载这个快照,比重新读取、解析、编译原头文件的成本低得多;但对应的代价是,只要预编译头包含的任意文件被修改,就必须重新生成预编译头,进而触发所有依赖它的翻译单元全量重编译。
问题1:是否需要把所有从不修改的标准/系统头文件都纳入预编译头?
从增量构建的角度看,完全没有理由不这么做。这些标准C/C++、POSIX头文件几乎不会被修改(除非你升级编译器或系统库,但这种场景下本来就需要全量重编译),把它们全部放进预编译头后,每次增量构建时所有翻译单元都能直接复用预编译好的状态,彻底省去重复解析标准库头文件的巨大开销——这正是预编译头最能发挥价值的场景之一。
既然你不在意预编译头的一次性生成成本,那就更没问题了:一次性的编译时间投入,能换来后续无数次增量构建的速度提升,非常划算。
问题2:使用预编译头时,自定义轻量头文件是否还有编译速度优势?
结论是:这种做法几乎没有意义,甚至可能适得其反。
你担心的“编译器状态负载增加”在实际场景中基本可以忽略——现代编译器的符号管理、重载决议逻辑都经过高度优化,标准库头文件带来的符号量并不会对这些操作造成可感知的性能影响。反而,如果你为了一点点功能单独写自定义头文件,会增加额外的文件IO和少量解析成本;更关键的是,预编译头已经把整个<type_traits>都预编译好了,直接引用<type_traits>的成本和引用你的自定义is_same.hpp几乎没有区别,甚至前者因为已经在预编译状态里,成本更低。
只有一种极端情况可能例外:你的预编译头没有包含<type_traits>,而你只有极少数翻译单元需要std::is_same——这时自定义轻量头文件能避免把整个<type_traits>拖进预编译头(或者避免少数翻译单元单独解析<type_traits>)。但如果预编译头已经包含了<type_traits>,这种优化完全是多余的。
内容的提问来源于stack exchange,提问作者Joseph Garvin

