仮想継承とQ_OBJECTマクロが衝突する際のQtコンパイルエラー解決

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を使用して型変換を行うコードを生成します。仮想継承が絡む場合、この型変換が安全に行えず、エラーが発生します。シグナルを基底クラスに集約するか、継承関係を再設計することで回避可能です。

タグ: Qt MOC 仮想継承 static_cast Q_OBJECT

6月6日 18:00 投稿