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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 06:14:51