Qtフレームワークでクラス階層を設計する際、仮想継承とQ_OBJECTマクロを併用することで、特定のコンパイルエラーが発生するケースがあります。本記事では、その問題の再現方法と対処法を解説します。
1. コンソールプロジェクトの作成
まず、以下の.proファイルを使用してQtコンソールアプリケーションを作成します。
QT -= gui
CONFIG += c++11 console
CONFIG -= app_bundle
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
main.cpp \
baseclass.cpp \
subclass.cpp \
subclass2.cpp
HEADERS += \
baseclass.h \
subclass.h \
subclass2.h
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
2. 基底クラスbaseClassの定義
baseclass.h
#ifndef BASECLASS_H
#define BASECLASS_H
#include "QObject"
class baseClass: public QObject
{
Q_OBJECT
public:
baseClass(QObject * p = nullptr);
virtual ~baseClass() {}
virtual void vMethod(int) = 0;
signals:
void signal1();
};
#endif // BASECLASS_H
baseclass.cpp
#include "baseclass.h"
baseClass::baseClass(QObject * p) : QObject(p)
{
}
3. 派生クラスsubClass(仮想継承)
subclass.h
#ifndef SUBCLASS_H
#define SUBCLASS_H
#include "baseclass.h"
class subClass: virtual public baseClass
{
Q_OBJECT
public:
subClass(QObject * p = nullptr);
virtual ~subClass() {};
virtual void vMethod(int) = 0;
signals:
void signal2();
};
#endif // SUBCLASS_H
subclass.cpp
#include "subclass.h"
subClass::subClass(QObject * p) : baseClass(p)
{
}
4. 別の派生クラスsubClass2(仮想継承)
subclass2.h
#ifndef SUBCLASS2_H
#define SUBCLASS2_H
#include "baseclass.h"
class subClass2 : virtual public baseClass
{
Q_OBJECT
public:
subClass2(QObject * p = nullptr);
virtual void vMethod(int) = 0;
};
#endif // SUBCLASS2_H
subclass2.cpp
#include "subclass2.h"
subClass2::subClass2(QObject * p) : baseClass(p)
{
}
5. main.cppの実装
#include <QCoreApplication>
#include "subclass.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
return a.exec();
}
発生するエラー
上記コードをビルドすると、以下のようなコンパイルエラーが発生します。
/build/testQtSignalBug-Qt5_9_4_debug/moc_subclass.cpp:68: error: cannot convert from pointer to base class 'QObject' to pointer to derived class 'subClass' via virtual base 'baseClass'
subClass *_t = static_cast<subClass *>(_o);
解決方法
この問題は、仮想継承を使用している派生クラスでQ_OBJECTマクロとシグナルを同時に宣言していることが原因です。以下のようにsubClass.hからsignalsセクションを削除することで解決できます。
修正後のsubclass.h
#ifndef SUBCLASS_H
#define SUBCLASS_H
#include "baseclass.h"
class subClass: virtual public baseClass
{
Q_OBJECT
public:
subClass(QObject * p = nullptr);
virtual ~subClass() {};
virtual void vMethod(int) = 0;
};
#endif // SUBCLASS_H
原因の考察
QtのMOC(Meta-Object Compiler)は、Q_OBJECTマクロを含むクラスに対して、内部的にstatic_castを使用して型変換を行うコードを生成します。仮想継承が絡む場合、この型変換が安全に行えず、エラーが発生します。シグナルを基底クラスに集約するか、継承関係を再設計することで回避可能です。