如何修复Clang与Qt 6环境下多继承Q_GADGET导致的编译失败问题?
我帮你分析下这个问题:你在GitHub Actions的macos-11环境用Qt6.3.1编译JKQtPlotter时碰到的在不同类型的多个基类中发现成员‘qt_check_for_QGADGET_macro’错误,本质是多个带Q_GADGET的基类被同一子类继承后,Clang编译器对同名成员函数产生了歧义。
问题根源
Q_GADGET宏会自动展开生成一个名为qt_check_for_QGADGET_macro()的成员函数。当你的子类(比如JKQTPBarVerticalGraph)同时继承了多个带有Q_GADGET的基类(比如jkqtpgraphsbasestylingmixins.h和jkqtpgraphsbaseerrors.h里的类),编译器在查找这个函数时,会因为多个基类都有同名函数而不知道该选哪一个,从而触发编译失败。这种情况在Qt6早期版本(比如6.3.1)搭配macOS的Clang编译器时更容易出现。
你提供的测试代码完美复现了这个问题:
#pragma once #include <QObject> class Gadget1 { Q_GADGET }; class Gadget2 { Q_GADGET }; class Object1 : public QObject { Q_OBJECT }; class Check : public Object1 , public Gadget1 , public Gadget2 { Q_OBJECT };
用Clang的MOC工具编译这段代码,就能看到完全一致的错误提示。
可行的解决方法
1. 修改JKQtPlotter的基类设计(推荐,根治问题)
找到那些同时被继承的带Q_GADGET的基类,把Q_GADGET合并到一个公共基类中,避免多个基类各自定义这个宏。比如:
- 创建一个公共基类
JKQTPBaseGadget,只包含Q_GADGET; - 让原来的
JKQTPGraphsBaseStylingMixin、JKQTPBarVerticalErrorGraph等类都继承JKQTPBaseGadget,而不是各自添加Q_GADGET。
这样子类继承多个基类时,qt_check_for_QGADGET_macro()只会来自一个公共基类,就不会有歧义了。
2. 添加Clang编译选项(快速 workaround)
在CMake配置中给JKQtPlotter的目标添加-fms-extensions编译选项,这个选项让Clang兼容一些MSVC的扩展行为,能自动处理这种多继承下的同名成员歧义:
target_compile_options(JKQTPlotterSharedLib PRIVATE -fms-extensions)
你可以在GitHub Actions的构建脚本里,针对JKQtPlotter子项目添加这个配置。
3. 升级Qt版本
这个问题在Qt6的后续版本(比如6.4及以上)已经被修复了。你可以尝试在GitHub Actions中切换到更新的Qt版本(比如6.5.x)来构建项目,大概率能直接解决这个错误。
4. 临时修改MOC生成代码(应急用,不推荐长期使用)
如果暂时没法改源码或升级Qt,可以在GitHub Actions的构建流程中,在MOC生成代码之后、编译之前,手动修改报错的moc文件,明确指定调用某个基类的qt_check_for_QGADGET_macro。比如把&T::qt_check_for_QGADGET_macro改成&JKQTPGraphsBaseStylingMixin::qt_check_for_QGADGET_macro(替换成实际的基类名)。不过这种方法维护性很差,后续源码更新后容易失效,只适合临时应急。
内容的提问来源于stack exchange,提问作者Igor Mironchik

