C++20模块在极简Qt应用中引发编译错误
C++20模块与Qt混合编译的冲突问题
我正在学习C++20模块特性,从标准库模块入手,同时开发一个基于Qt的简单GUI应用。除了CMake对模块的支持本身不完善外,还遇到了模块引发的奇怪编译错误,找不到问题根源。
最小复现示例
CMakeLists.txt
cmake_minimum_required(VERSION 3.28.0) project(TestProj VERSION 0.1.0 LANGUAGES CXX) set(OUTPUT_BIN_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED True) SET(CMAKE_CXX_FLAGS "-fmodules-ts ${CMAKE_CXX_FLAGS}") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_BIN_DIRECTORY}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_BIN_DIRECTORY}) # add_custom_target( # std_modules ALL # COMMAND ${CMAKE_CXX_COMPILER} -std=c++20 -g -fmodules-ts -xc++-system-header memory # ) find_package(Qt5 REQUIRED COMPONENTS Widgets) add_executable(TestApp main.cpp) #add_dependencies(TestApp std_modules) target_link_libraries(TestApp Qt5::Widgets)
main.cpp
#include <QApplication> #include <QLabel> int main(int argc, char **argv) { QApplication app(argc, argv); QLabel hello("Hello world!"); hello.show(); return app.exec(); }
错误触发场景
当保持上述代码(注释掉自定义目标std_modules及其依赖)时,编译完全正常。但如果想要以模块形式导入标准库头文件(比如import <memory>;),需要预编译这些头文件——这就是自定义目标std_modules的作用。但一旦取消注释该目标和依赖(甚至还没在代码中添加import <memory>;),编译就会失败,出现大量错误:
[main] Building folder: testproj [build] Starting build [proc] Executing command: /usr/bin/cmake --build /ramdisk/testproj/build --config Debug --target all -- [build] [1/3 33% :: 0.575] cd /ramdisk/testproj/build && /usr/bin/g++ -std=c++20 -g -fmodules-ts -xc++-system-header memory [build] [2/3 66% :: 1.859] Building CXX object CMakeFiles/TestApp.dir/main.cpp.o [build] FAILED: CMakeFiles/TestApp.dir/main.cpp.o [build] /usr/bin/g++ -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -isystem /usr/include/qt -isystem /usr/include/qt/QtWidgets -isystem /usr/include/qt/QtGui -isystem /usr/include/qt/QtCore -isystem /usr/lib/qt/mkspecs/linux-g++ -fmodules-ts -g -std=gnu++20 -fPIC -MD -MT CMakeFiles/TestApp.dir/main.cpp.o -MF CMakeFiles/TestApp.dir/main.cpp.o.d -o CMakeFiles/TestApp.dir/main.cpp.o -c /ramdisk/testproj/main.cpp [build] In file included from /usr/include/c++/13.2.1/bits/stl_tempbuf.h:63, [build] from /usr/include/c++/13.2.1/memory:66, [build] of module /usr/include/c++/13.2.1/memory, imported at /usr/include/qt/QtCore/qsharedpointer_impl.h:71, [build] included from /usr/include/qt/QtCore/qsharedpointer.h:48, [build] from /usr/include/qt/QtGui/qpixmap.h:48, [build] from /usr/include/qt/QtGui/qbrush.h:52, [build] from /usr/include/qt/QtGui/qpalette.h:46, [build] from /usr/include/qt/QtWidgets/qwidget.h:48, [build] from /usr/include/qt/QtWidgets/qframe.h:44, [build] from /usr/include/qt/QtWidgets/qlabel.h:44, [build] from /usr/include/qt/QtWidgets/QLabel:1, [build] from /ramdisk/testproj/main.cpp:2: [build] /usr/include/c++/13.2.1/ext/numeric_traits.h: In instantiation of ‘struct __gnu_cxx::__numeric_traits_integer<short unsigned int>’: [build] /usr/include/c++/13.2.1/charconv:533:69: required from ‘constexpr bool std::__detail::__from_chars_alnum(const char*&, const char*, _Tp&, int) [with bool _DecOnly = true; _Tp = short unsigned int]’ [build] /usr/include/c++/13.2.1/format:277:42: required from ‘constexpr std::pair<short unsigned int, const _CharT*> std::__format::__parse_integer(const _CharT*, const _CharT*) [with _CharT = char]’ [build] /usr/include/c++/13.2.1/format:288:49: required from ‘constexpr std::pair<short unsigned int, const _CharT*> std::__format::__parse_integer(const _CharT*, const _CharT*) [with _CharT = wchar_t]’ [build] /usr/include/c++/13.2.1/format:471:51: required from ‘static constexpr std::__format::_Spec<_CharT>::iterator std::__format::_Spec<_CharT>::_S_parse_width_or_precision(iterator, iterator, short unsigned int&, bool&, std::basic_format_parse_context<_CharT>&) [with _CharT = wchar_t; iterator = const wchar_t*]’ [build] /usr/include/c++/13.2.1/format:509:43: required from ‘constexpr std::__format::_Spec<_CharT>::iterator std::__format::_Spec<_CharT>::_M_parse_width(iterator, iterator, std::basic_format_parse_context<_CharT>&) [with _CharT = wchar_t; iterator = const wchar_t*]’ [build] /usr/include/c++/13.2.1/format:904:33: required from ‘constexpr typename std::basic_format_parse_context<_CharT>::iterator std::__format::__formatter_int<_CharT>::_M_do_parse(std::basic_format_parse_context<_CharT>&, std::__format::_Pres_type) [with _CharT = wchar_t; typename std::basic_format_parse_context<_CharT>::iterator = const wchar_t*]’ [build] /usr/include/c++/13.2.1/format:987:21: required from ‘constexpr typename std::basic_format_parse_context<_CharT>::iterator std::__format::__formatter_int<_CharT>::_M_parse(std::basic_format_parse_context<_CharT>&) [with _Tp = char; _CharT = wchar_t; typename std::basic_format_parse_context<_CharT>::iterator = const wchar_t*]’ [build] /usr/include/c++/13.2.1/format:1777:28: required from here [build] /usr/include/c++/13.2.1/ext/numeric_traits.h:130:3: error: definition of ‘enum __gnu_cxx::__is_integer_nonstrict<__int128 unsigned>::<unnamed>’ does not match [build] 130 | _GLIBCXX_INT_N_TRAITS(__int128, 128) [build] | ^~~~~~~~~~~~~~~~~~~~~ [build] In file included from /usr/include/c++/13.2.1/utility:79, [build] from /usr/include/qt/QtCore/qglobal.h:47, [build] from /usr/include/qt/QtGui/qtguiglobal.h:43, [build] from /usr/include/qt/QtWidgets/qtwidgetsglobal.h:43, [build] from /usr/include/qt/QtWidgets/qapplication.h:43, [build] from /usr/include/qt/QtWidgets/QApplication:1, [build] from /ramdisk/testproj/main.cpp:1: [build] /usr/include/c++/13.2.1/ext/numeric_traits.h:57:12: note: existing definition ‘enum __gnu_cxx::__is_integer_nonstrict<__int128 unsigned>::<unnamed>’ [build] 57 | enum { __width = __value ? sizeof(_Tp) * __CHAR_BIT__ : 0 }; [build] | ^ [build] /usr/include/c++/13.2.1/ext/numeric_traits.h:130:3: note: ... this enumerator ‘__gnu_cxx::__is_integer_nonstrict<__int128 unsigned>::__value’ [build] 130 | _GLIBCXX_INT_N_TRAITS(__int128, 128) [build] | ^~~~~~~~~~~~~~~~~~~~~ [build] /usr/include/c++/13.2.1/ext/numeric_traits.h:57:14: note: enumerator ‘__gnu_cxx::__is_integer_nonstrict<__int128 unsigned>::__width’ does not match ... [build] 57 | enum { __width = __value ? sizeof(_Tp) * __CHAR_BIT__ : 0 }; [build] | ^~~~~~~ [build] /usr/include/c++/13.2.1/ext/numeric_traits.h:64:53: note: during load of pendings for ‘__gnu_cxx::__is_integer_nonstrict’ [build] 64 | static_assert(__is_integer_nonstrict<_Value>::__value, [build] | ^~~~~~~ [build] ninja: build stopped: subcommand failed. [proc] The command: /usr/bin/cmake --build /ramdisk/testproj/build --config Debug --target all -- exited with code: 1 [driver] Build completed: 00:00:01.892 [build] Build finished with exit code 1
问题分析
看起来问题出在Qt头文件以传统方式包含memory头时,被自动映射为模块导入,导致了符号定义冲突。这应该不是Qt特有的问题,而是C++20模块与传统头文件混合使用时的通用冲突问题,但它直接导致我无法用模块开发应用。
我可以选择放弃模块形式导入标准库,回到旧的头文件包含方式,但模块的核心目标之一就是减少头文件依赖、提升编译效率,这只是临时的变通,不是真正的解决方案。
内容的提问来源于stack exchange,提问作者PookyFan
相关产品推荐
相关产品推荐

