库PRIVATE预编译头在可执行文件中失效,伴std::jthread编译错误求方案
C++库预编译头PRIVATE配置下依赖目标编译失败的替代解决方案
我开发C++库voidmtlib时,使用CMake的target_precompile_headers并指定PRIVATE关键字配置预编译头,但将该库引入示例可执行文件Sandbox后,出现编译错误,只有把PRIVATE改为PUBLIC才能解决问题——但这不符合库开发的最佳实践。请问除了将target_precompile_headers设为PUBLIC外,还有其他解决方案吗?
库的CMake代码(voidmtlib/CMakeLists.txt)
# voidmtlib/CMakeLists.txt set(src_core_sourcefiles "src/core/voidmtlib_core.cpp" "src/core/types/base/base_task.cpp" "src/core/types/base/base_thread.cpp") set(src_core_headerfiles "include/voidmtlib/core/voidmtlib_core.h" "include/voidmtlib/core/types/base/base_task.h" "include/voidmtlib/core/types/base/base_thread.h") set(src_utils_sourcefiles "src/utils/logger/logger.cpp") set(src_utils_headerfiles "include/voidmtlib/utils/logger/logger.h") set(voidmtlib_public_include_directories "include") set(voidmtlib_private_include_directories "include" "include/voidmtlib" "include/voidmtlib/core" "include/voidmtlib/core/types" "include/voidmtlib/core/types/base" "include/voidmtlib/utils" "include/voidmtlib/utils/logger") set(dependency_list "spdlog") set(method_list "find_package;add_subdirectory") dependency_manager("${dependency_list}" "${method_list}") #message("${CMAKE_CURRENT_SOURCE_DIR}/${src_core_include}") if(${USE_FetchContent} AND NOT ${spdlog_dependency_enabled}) declare_and_make_available("spdlog" "https://github.com/gabime/spdlog.git" "v1.x") endif() set(all_src_files ${src_utils_sourcefiles} ${src_core_sourcefiles}) set(all_hdr_files ${src_utils_headerfiles} ${src_core_headerfiles}) #list(TRANSFORM all_src_files PREPEND ${CMAKE_CURRENT_LIST_DIR}/) #foreach (file IN LISTS all_src_files) # message(${file}) #endforeach () add_library(${PROJECT_NAME} ${all_src_files} ${all_hdr_files}) include(GNUInstallDirs) list(TRANSFORM voidmtlib_private_include_directories PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/) list(TRANSFORM voidmtlib_public_include_directories PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/) target_precompile_headers(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/precompiled/pch.h" "${CMAKE_CURRENT_SOURCE_DIR}/precompiled/pch.cpp") #target_sources(${PROJECT_NAME} PUBLIC ${all_hdr_files}) #target_sources(${PROJECT_NAME} PUBLIC ${all_src_files}) target_include_directories(${PROJECT_NAME} PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>") target_include_directories(${PROJECT_NAME} PRIVATE "${voidmtlib_private_include_directories}") target_link_libraries(${PROJECT_NAME} PRIVATE spdlog::spdlog) #include(CMakePrintHelpers) #cmake_print_properties(TARGETS ${PROJECT_NAME} PROPERTIES INCLUDE_DIRECTORIES)
编译错误信息
====================[ Build | voidmtlib | Internal_Linux_Ninja_Clang_Clion - Clang_Clion_Debug ]==== /bin/cmake --build --target voidmtlib --config Debug --preset Clang_Clion_Debug [9/9] Linking CXX static library "/mnt/Main/Projects/C&C++/voidmtlib/build/Linux/Ninja Multi-Config/Internal_Linux_Ninja_Clang_Clion/liba/Debug/libvoidmtlib.a" Build finished ====================[ Build | Sandbox | Internal_Linux_Ninja_Clang_Clion - Clang_Clion_Debug ]==== /bin/cmake --build --target Sandbox --config Debug --preset Clang_Clion_Debug [8/9] Building CXX object voidmtlib_examples/Sandbox/CMakeFiles/Sandbox.dir/Debug/main.cpp.o FAILED: [code=1] voidmtlib_examples/Sandbox/CMakeFiles/Sandbox.dir/Debug/main.cpp.o /bin/clang++ -DCMAKE_INTDIR=\"Debug\" -I"/mnt/Main/Projects/C&C++/voidmtlib/voidmtlib/include" -g -std=gnu++20 -fcolor-diagnostics -MD -MT voidmtlib_examples/Sandbox/CMakeFiles/Sandbox.dir/Debug/main.cpp.o -MF voidmtlib_examples/Sandbox/CMakeFiles/Sandbox.dir/Debug/main.cpp.o.d @voidmtlib_examples/Sandbox/CMakeFiles/Sandbox.dir/Debug/main.cpp.o.modmap -o voidmtlib_examples/Sandbox/CMakeFiles/Sandbox.dir/Debug/main.cpp.o -c '/mnt/Main/Projects/C&C++/voidmtlib/voidmtlib_examples/Sandbox/main.cpp' In file included from /mnt/Main/Projects/C&C++/voidmtlib/voidmtlib_examples/Sandbox/main.cpp:2: In file included from /mnt/Main/Projects/C&C++/voidmtlib/voidmtlib_examples/Sandbox/include/sandbox.h:8: In file included from /mnt/Main/Projects/C&C++/voidmtlib/voidmtlib/include/voidmtlib/core/voidmtlib_core.h:8: /mnt/Main/Projects/C&C++/voidmtlib/voidmtlib/include/voidmtlib/core/types/base/base_thread.h:19:7: error: no type named 'jthread' in namespace 'std' 19 | std::jthread m_thread; | ~~~~~^ /mnt/Main/Projects/C&C++/voidmtlib/voidmtlib/include/voidmtlib/core/types/base/base_thread.h:20:7: error: no member named 'thread' in namespace 'std' 20 | std::thread::id m_thread_id = m_thread.get_id(); | ~~~~~^ 2 errors generated. ninja: build stopped: subcommand failed.
开发环境
- IDE:Clion
- 构建生成器:Ninja Multi-Config
项目文件结构
├── cmake │ ├── dependency_utils.cmake │ └── fetchcontent_utils.cmake ├── CMakeLists.txt ├── CMakePresets.json ├── CMakeUserPresets.json ├── README.md ├── voidmtlib │ ├── CMakeLists.txt │ ├── include │ │ └── voidmtlib │ │ ├── core │ │ │ ├── types │ │ │ │ └── base │ │ │ │ ├── base_task.h │ │ │ │ └── base_thread.h │ │ │ └── voidmtlib_core.h │ │ └── utils │ │ └── logger │ │ └── logger.h │ ├── precompiled │ │ ├── pch.cpp │ │ └── pch.h │ ├── src │ │ ├── core │ │ │ ├── types │ │ │ │ └── base │ │ │ │ ├── base_task.cpp │ │ │ │ └── base_thread.cpp │ │ │ └── voidmtlib_core.cpp │ │ └── utils │ │ └── logger │ │ └── logger.cpp │ └── vendor └── voidmtlib_examples ├── CMakeLists.txt ├── README.md └── Sandbox ├── CMakeLists.txt ├── include │ └── sandbox.h └── main.cpp
Sandbox的CMake代码
#Sandbox/Cmakelists.txt add_executable(Sandbox main.cpp include/sandbox.h) target_link_libraries(Sandbox PRIVATE voidmtlib)
解决方案
1. 修复库的公开头文件自包含性
编译错误的核心原因是:库的公开头文件(如base_thread.h)依赖了预编译头中包含的<thread>等头文件,但PRIVATE级别的预编译头不会传递给依赖目标(Sandbox)。解决方法是:
- 在每个公开头文件中显式包含所需的依赖头文件,确保头文件可以独立编译。比如在
base_thread.h开头添加:#include <thread>
这样Sandbox编译时,就能直接找到std::jthread和std::thread的定义,不需要依赖库的预编译头。
2. 为Sandbox单独配置预编译头
如果希望Sandbox也能利用预编译头加速编译,可以在Sandbox的CMakeLists.txt中单独设置预编译头:
# Sandbox/CMakeLists.txt add_executable(Sandbox main.cpp include/sandbox.h) target_link_libraries(Sandbox PRIVATE voidmtlib) # 引入库的预编译头供Sandbox使用 target_precompile_headers(Sandbox PRIVATE "${CMAKE_SOURCE_DIR}/voidmtlib/precompiled/pch.h")
这种方式既保持了库的PRIVATE预编译头配置(符合最佳实践),又能让Sandbox享受到预编译头的优化。
3. 分离预编译头中的公共依赖(进阶)
如果库的预编译头包含了大量公共依赖,可以将这部分内容拆分到一个单独的公共头文件中,比如public_pch.h,然后:
- 在库的PRIVATE预编译头中包含这个公共头文件;
- 让库的公开头文件显式包含
public_pch.h,或者通过target_include_directories将其暴露给依赖目标。
这种方式可以减少重复代码,同时保持预编译头的PRIVATE属性。
内容的提问来源于stack exchange,提问作者ALEPHVOID
相关产品推荐
相关产品推荐

