You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

库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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 01:28:14